<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"vchao.top","root":"/","scheme":"Muse","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
<meta property="og:type" content="website">
<meta property="og:title" content="乘风破浪">
<meta property="og:url" content="http://vchao.top/page/2/index.html">
<meta property="og:site_name" content="乘风破浪">
<meta property="og:description" content="怀揣好奇，梦想改变世界的菜鸟极客">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="vchao">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://vchao.top/page/2/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>乘风破浪</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">乘风破浪</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E6%89%8B%E6%8A%8A%E6%89%8B%E5%B8%A6%E4%BD%A0%E6%92%B8%E4%B8%80%E4%B8%AA%E6%A0%A1%E5%9B%ADAPP%EF%BC%88%E4%BA%94%EF%BC%89%EF%BC%9A%E6%96%B0%E9%97%BB%E4%B8%AD%E5%BF%83%E6%A8%A1%E5%9D%97/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E6%89%8B%E6%8A%8A%E6%89%8B%E5%B8%A6%E4%BD%A0%E6%92%B8%E4%B8%80%E4%B8%AA%E6%A0%A1%E5%9B%ADAPP%EF%BC%88%E4%BA%94%EF%BC%89%EF%BC%9A%E6%96%B0%E9%97%BB%E4%B8%AD%E5%BF%83%E6%A8%A1%E5%9D%97/" class="post-title-link" itemprop="url">手把手带你撸一个校园APP（五）：新闻中心模块</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>这个项目是很早之前在学校做的，如今再回首。很多代码很是粗糙，逻辑也不尽完善。还望各位看官海涵。</p>
</blockquote>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>通过上一篇文章的功能设计，我们可以发现新闻通知公告等是APP的最主要功能点。主要是聚合展示学校官网的通知公告，官方微信，微博的重要信息，学校社团的活动，以及学校优秀自媒体的文章等等。</p>
<p><strong>系列文章:</strong></p>
<ol>
<li><a target="_blank" rel="noopener" href="https://vchao.blog.csdn.net/article/details/80787263">手把手带你撸一个校园APP（一）：项目简介</a></li>
<li><a target="_blank" rel="noopener" href="https://vchao.blog.csdn.net/article/details/81456617">手把手带你撸一个校园APP（二）：应用启动和欢迎页面</a></li>
<li><a target="_blank" rel="noopener" href="https://vchao.blog.csdn.net/article/details/81837608">手把手带你撸一个校园APP（三）：用户模块（登录注册等）</a></li>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/zheng_weichao/article/details/104209803">手把手带你撸一个校园APP（四）：APP框架及功能设计</a></li>
<li>手把手带你撸一个校园APP（五）：新闻中心模块</li>
<li>手把手带你撸一个校园APP（六）：失物招领&amp;二手交易模块</li>
<li>手把手带你撸一个校园APP（七）：图说校园模块</li>
<li>手把手带你撸一个校园APP（八）：校园通讯录模块</li>
<li>手把手带你撸一个校园APP（九）：课程表模块（模拟登陆爬取教务处课程信息）</li>
<li>手把手带你撸一个校园APP（十）：APP通用模块（更新，意见反馈等）</li>
</ol>
<h2 id="实现效果"><a href="#实现效果" class="headerlink" title="实现效果"></a>实现效果</h2><p>新闻部分主要是分了“通知公告”，“新闻速递”，“自媒体”，“校园文化”四个栏目，之间可以通过左右滑动进行切换。效果图如下：<br><img src="https://img-blog.csdnimg.cn/20200208211236360.gif" alt="科师有约新闻模块"></p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>其实这是挺常见的一个新闻资讯模块。我们也可以轻易得出以下内容：</p>
<ol>
<li>新闻条目 一般都有标题，配图，作者，发表时间等组成。我们的新闻只是聚合展示使用，可以外加一个资讯链接；</li>
<li>配图数量并不一致，有无图的，1 张配图的，以及 3 张配图的。我们需要根据配图的数量进行不同布局的设置；</li>
<li>新闻列表应该拥有下拉刷新，上拉加载的功能。</li>
</ol>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>本项目基于Bmob进行开发，很多地方需要Bmob相关知识。大家如果没了解过的话，可先自行查看 <a target="_blank" rel="noopener" href="http://doc.bmob.cn/data/android/develop_doc/">Bmob开发文档</a> 。</p>
<h3 id="数据库"><a href="#数据库" class="headerlink" title="数据库"></a>数据库</h3><p>当初设计数据库字段的时候，很多东西都为了简单，没有做更合理的规划。字段如下：<br>| 字段名            | 描述   | 类型    | 主键 |<br>| —————– | —— | ——- | —- |<br>| objectId          | 用户id | String  | 是   |<br>| title | 标题| String  |      |<br>| author| 作者 | String  |      |<br>| time              | 发布时间| String  |      |<br>|kind|栏目分类|String||<br>| content| 内容链接| String  |      |<br>|pic1             | 缩略图一| String  |      |<br>| pic2               | 缩略图二   | String  |      |<br>| pic3             | 缩略图三  | String  |      |<br>| tag              | 布局样式（图片数量）| String  |      |</p>
<blockquote>
<p>大家如果自己从头来的话，建议适当更改部分字段的类型以及组合方式，会更合理些。比如time改为时间类型，图片改为列表等。本文暂且不表。</p>
</blockquote>
<h3 id="安卓"><a href="#安卓" class="headerlink" title="安卓"></a>安卓</h3><h4 id="逻辑流程讲解"><a href="#逻辑流程讲解" class="headerlink" title="逻辑流程讲解"></a>逻辑流程讲解</h4><p>本模块逻辑设计如下：</p>
<ol>
<li>使用CoordinatorTabLayout + ViewPager + Fragment 完成新闻模块主页面的显示；</li>
<li>Fragment 进行网络请求，获取新闻数据并展示，使用 RecyclerView 展示新闻列表，根据缩略图数量对应3种条目布局；</li>
<li>新闻详情页面使用WebView 用于展示新闻详情。</li>
</ol>
<h4 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h4><p>在这里为了能快速开发，我们使用了 CoordinatorTabLayout 。</p>
<blockquote>
<p>文章：<a target="_blank" rel="noopener" href="http://hugeterry.cn/dreams/559">CoordinatorTabLayout组件库</a><br>Github地址：<a target="_blank" rel="noopener" href="https://github.com/hugeterry/CoordinatorTabLayout">CoordinatorTabLayout</a><br> 作者：hugeterry</p>
</blockquote>
<h5 id="使用-CoordinatorTabLayout-搭建新闻主页面"><a href="#使用-CoordinatorTabLayout-搭建新闻主页面" class="headerlink" title="使用 CoordinatorTabLayout 搭建新闻主页面"></a>使用 CoordinatorTabLayout 搭建新闻主页面</h5><p>使用步骤如下：</p>
<ol>
<li><p>在 app 的 build.gradle 中添加依赖</p>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">dependencies</span> &#123;</span><br><span class="line">    implementation <span class="string">&#x27;cn.hugeterry.coordinatortablayout:coordinatortablayout:1.2.2&#x27;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li><p> 布局文件中使用</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">RelativeLayout</span> <span class="attr">xmlns:android</span>=<span class="string">&quot;http://schemas.android.com/apk/res/android&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xmlns:app</span>=<span class="string">&quot;http://schemas.android.com/apk/res-auto&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">cn.hugeterry.coordinatortablayout.CoordinatorTabLayout</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:id</span>=<span class="string">&quot;@+id/cootablayout_news&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">        <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">android.support.v4.view.ViewPager</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:id</span>=<span class="string">&quot;@+id/vp_news&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_width</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">android:layout_height</span>=<span class="string">&quot;match_parent&quot;</span></span></span><br><span class="line"><span class="tag">            <span class="attr">app:layout_behavior</span>=<span class="string">&quot;@string/appbar_scrolling_view_behavior&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">cn.hugeterry.coordinatortablayout.CoordinatorTabLayout</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">RelativeLayout</span>&gt;</span></span><br></pre></td></tr></table></figure></li>
<li><p>在 java 代码中使用</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 四个栏目标题</span></span><br><span class="line">String[] mTitles = &#123;<span class="string">&quot;通知公告&quot;</span>, <span class="string">&quot;新闻速递&quot;</span>, <span class="string">&quot;自媒体&quot;</span>, <span class="string">&quot;校园文化&quot;</span>&#125;</span><br><span class="line"><span class="comment">// 初始化 Fragment 列表</span></span><br><span class="line">ArrayList&lt;Fragment&gt; mFragments = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">mFragments.add(MainFragment1.getInstance(mTitles[<span class="number">0</span>]));</span><br><span class="line">mFragments.add(MainFragment2.getInstance(mTitles[<span class="number">1</span>]));</span><br><span class="line">mFragments.add(MainFragment3.getInstance(mTitles[<span class="number">2</span>]));</span><br><span class="line">mFragments.add(MainFragment4.getInstance(mTitles[<span class="number">3</span>]));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置Viewpager</span></span><br><span class="line">mViewPager.setOffscreenPageLimit(<span class="number">4</span>);</span><br><span class="line">mViewPager.setAdapter(<span class="keyword">new</span> MyPagerAdapter(getChildFragmentManager(), mFragments, mTitles));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置顶部 展开后 4 张背景图</span></span><br><span class="line">mImageArray = <span class="keyword">new</span> <span class="keyword">int</span>[]&#123;</span><br><span class="line">        R.mipmap.keshi1,</span><br><span class="line">        R.mipmap.keshi4,</span><br><span class="line">        R.mipmap.img_bg_news3,</span><br><span class="line">        R.mipmap.img_bg_news4&#125;;</span><br><span class="line"><span class="comment">// 设置顶部 收缩后 4 种背景色</span></span><br><span class="line">mColorArray = <span class="keyword">new</span> <span class="keyword">int</span>[]&#123;</span><br><span class="line">        android.R.color.holo_blue_light,</span><br><span class="line">        android.R.color.holo_red_light,</span><br><span class="line">        android.R.color.holo_orange_light,</span><br><span class="line">        android.R.color.holo_green_light&#125;;</span><br><span class="line"><span class="comment">// 设置 CoordinatorTabLayout 属性</span></span><br><span class="line">mCoordinatorTabLayout.setTransulcentStatusBar(mActivity)</span><br><span class="line">        .setTitle(<span class="string">&quot;&quot;</span>)</span><br><span class="line">        .setImageArray(mImageArray, mColorArray)</span><br><span class="line">        .setupWithViewPager(mViewPager);</span><br></pre></td></tr></table></figure></li>
</ol>
<h5 id="网络请求，数据填充"><a href="#网络请求，数据填充" class="headerlink" title="网络请求，数据填充"></a>网络请求，数据填充</h5><ol>
<li>建立数据库对应表 bean 类<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NewsBean</span> <span class="keyword">extends</span> <span class="title">BmobObject</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    <span class="keyword">private</span> String author;</span><br><span class="line">    <span class="keyword">private</span> String time;</span><br><span class="line">    <span class="keyword">private</span> String tag;</span><br><span class="line">    <span class="keyword">private</span> String pic1;</span><br><span class="line">    <span class="keyword">private</span> String pic2;</span><br><span class="line">    <span class="keyword">private</span> String pic3;</span><br><span class="line">    <span class="keyword">private</span> String content;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ....get set 方法</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>网络请求数据，并填充<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">mAdapter = <span class="keyword">new</span> NNNAdapter();</span><br><span class="line">newsBeanList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">BmobQuery&lt;NewsBean&gt; query = <span class="keyword">new</span> BmobQuery&lt;NewsBean&gt;();</span><br><span class="line">query.order(<span class="string">&quot;-time&quot;</span>);</span><br><span class="line"><span class="comment">// kind: [a=&quot;通知公告&quot;, b=&quot;新闻速递&quot;, c=&quot;自媒体&quot;, d=&quot;校园文化&quot;]</span></span><br><span class="line">query.addWhereEqualTo(<span class="string">&quot;kind&quot;</span>, <span class="string">&quot;a&quot;</span>);</span><br><span class="line">query.setLimit(<span class="number">10</span>);</span><br><span class="line">query.findObjects(<span class="keyword">new</span> FindListener&lt;NewsBean&gt;() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">done</span><span class="params">(List&lt;NewsBean&gt; object, BmobException e)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (e == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (NewsBean newsBean : object) &#123;</span><br><span class="line">                newsBeanList.add(newsBean);</span><br><span class="line">                LogUtils.e(newsBean.getAuthor());</span><br><span class="line">            &#125;</span><br><span class="line">            mAdapter.setData(newsBeanList);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line">mRecyclerView.setAdapter(mAdapter);</span><br></pre></td></tr></table></figure></li>
<li>图片数量不同对应不同的布局<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NewsBean</span> <span class="keyword">extends</span> <span class="title">BmobObject</span> <span class="keyword">implements</span> <span class="title">IMultiItem</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getLayoutRes</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">&quot;3&quot;</span>.equals(tag)) &#123;</span><br><span class="line">            <span class="keyword">return</span> R.layout.item_news_type3;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">&quot;2&quot;</span>.equals(tag)) &#123;</span><br><span class="line">            <span class="keyword">return</span> R.layout.item_news_type2;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> R.layout.item_news_type1;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 省略其它...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>新闻详情页面<br>Fragment 中给 RecycleView 设置点击事件，传递网址到详情Activity<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">mAdapter.setOnItemClickListener(<span class="keyword">new</span> OnItemClickListener() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onItemClick</span><span class="params">(<span class="meta">@NonNull</span> View view, <span class="keyword">int</span> position)</span> </span>&#123;</span><br><span class="line">        Intent intent = <span class="keyword">new</span> Intent(getActivity(), NewsWebActivity.class);</span><br><span class="line">        intent.putExtra(<span class="string">&quot;url&quot;</span>, newsBeanList.get(position).getContent());</span><br><span class="line">        startActivity(intent);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
网页详情页面 NewsActivity 中只有一个WebView。接收上个页面传递过来的参数，并且展示页面。这里做了一些初始化操作，大家在别处需要的话，也可以直接复制过去<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">String url = getIntent().getStringExtra(<span class="string">&quot;url&quot;</span>);</span><br><span class="line"><span class="keyword">if</span> (TextUtils.isEmpty(url)) &#123;</span><br><span class="line">    Toast.makeText(<span class="keyword">this</span>, <span class="string">&quot;该新闻已经失效！&quot;</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line">MyWebViewClient myWebViewClient = <span class="keyword">new</span> MyWebViewClient();</span><br><span class="line"><span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.LOLLIPOP) &#123;</span><br><span class="line">    wvNews.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);</span><br><span class="line">&#125;</span><br><span class="line">wvNews.getSettings().setJavaScriptCanOpenWindowsAutomatically(<span class="keyword">true</span>);<span class="comment">//设置js可以直接打开窗口，如window.open()，默认为false</span></span><br><span class="line">wvNews.getSettings().setJavaScriptEnabled(<span class="keyword">true</span>);<span class="comment">//是否允许执行js，默认为false。设置true时，会提醒可能造成XSS漏洞</span></span><br><span class="line">wvNews.getSettings().setSupportZoom(<span class="keyword">true</span>);<span class="comment">//是否可以缩放，默认true</span></span><br><span class="line">wvNews.getSettings().setBuiltInZoomControls(<span class="keyword">true</span>);<span class="comment">//是否显示缩放按钮，默认false</span></span><br><span class="line">wvNews.getSettings().setUseWideViewPort(<span class="keyword">true</span>);<span class="comment">//设置此属性，可任意比例缩放。大视图模式</span></span><br><span class="line">wvNews.getSettings().setLoadWithOverviewMode(<span class="keyword">true</span>);<span class="comment">//和setUseWideViewPort(true)一起解决网页自适应问题</span></span><br><span class="line">wvNews.getSettings().setAppCacheEnabled(<span class="keyword">true</span>);<span class="comment">//是否使用缓存</span></span><br><span class="line">wvNews.getSettings().setDomStorageEnabled(<span class="keyword">true</span>);<span class="comment">//DOM Storage</span></span><br><span class="line">wvNews.setWebChromeClient(<span class="keyword">new</span> WebChromeClient());</span><br><span class="line">wvNews.setWebViewClient(myWebViewClient);</span><br><span class="line">wvNews.loadUrl(url);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ....</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">MyWebViewClient</span> <span class="keyword">extends</span> <span class="title">WebViewClient</span> </span>&#123;</span><br><span class="line">    <span class="comment">//重写父类方法，让新打开的网页在当前的WebView中显示</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">shouldOverrideUrlLoading</span><span class="params">(WebView view, String url)</span> </span>&#123;</span><br><span class="line">        view.loadUrl(url);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure></li>
</ol>
<p>至此，新闻模块页面基本完成。还有就是本文挺长的，很感谢你可以看到这里。</p>
<p>如果本文对你有所帮助，还望可以随手赏一个点赞哈 ~ ~</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E6%89%8B%E6%8A%8A%E6%89%8B%E5%B8%A6%E4%BD%A0%E6%92%B8%E4%B8%80%E4%B8%AA%E6%A0%A1%E5%9B%ADAPP%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E5%92%8C%E6%AC%A2%E8%BF%8E%E9%A1%B5%E9%9D%A2/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E6%89%8B%E6%8A%8A%E6%89%8B%E5%B8%A6%E4%BD%A0%E6%92%B8%E4%B8%80%E4%B8%AA%E6%A0%A1%E5%9B%ADAPP%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9A%E5%BA%94%E7%94%A8%E5%90%AF%E5%8A%A8%E5%92%8C%E6%AC%A2%E8%BF%8E%E9%A1%B5%E9%9D%A2/" class="post-title-link" itemprop="url">手把手带你撸一个校园APP（二）：应用启动和欢迎页面</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>身为一个合格的APP，启动页面和欢迎页面是必不可少的。启动页面在每次启动时，可以做一些初始化之类的耗时操作，欢迎页面仅在用户第一次打开应用时展示，让用户对应用有一个大体的了解。</p>
<p>Github地址： <a target="_blank" rel="noopener" href="https://github.com/zhengweichao/Hevttc">科师有约校园APP</a></p>
<p><strong>系列文章：</strong></p>
<ol>
<li><a target="_blank" rel="noopener" href="https://vchao.blog.csdn.net/article/details/80787263">手把手带你撸一个校园APP（一）：项目简介</a></li>
<li><a target="_blank" rel="noopener" href="https://vchao.blog.csdn.net/article/details/81456617">手把手带你撸一个校园APP（二）：应用启动和欢迎页面</a></li>
<li><a target="_blank" rel="noopener" href="https://vchao.blog.csdn.net/article/details/81837608">手把手带你撸一个校园APP（三）：用户模块（登录注册等）</a></li>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/zheng_weichao/article/details/104209803">手把手带你撸一个校园APP（四）：APP框架及功能设计</a></li>
<li><a href="">手把手带你撸一个校园APP（五）：新闻页面中心模块</a></li>
<li>手把手带你撸一个校园APP（六）：失物招领&amp;二手交易模块</li>
<li>手把手带你撸一个校园APP（七）：图说校园模块</li>
<li>手把手带你撸一个校园APP（八）：校园通讯录模块</li>
<li>手把手带你撸一个校园APP（九）：课程表模块（模拟登陆爬取教务处课程信息）</li>
<li>手把手带你撸一个校园APP（十）：APP通用模块（更新，意见反馈等）</li>
</ol>
<h2 id="需求分析："><a href="#需求分析：" class="headerlink" title="需求分析："></a>需求分析：</h2><ol>
<li>用户点击图标，启动展示启动页面。</li>
<li>启动页面结束，如果是第一次打开应用，展示引导页面，否则进入登录页面。</li>
<li>登录页面和启动页面可以平滑切换。</li>
</ol>
<p>其实这部分内容本来也没多少东西，自己写的话，也不会耗费太多时间，但是网上已经有很多非常不错的开源框架，动画效果什么的已经可以满足需求了。本篇推荐使用 <a target="_blank" rel="noopener" href="https://github.com/stephentuso/welcome-android#adding-to-your-project">welcome-android</a> 框架。<br>身为一个欢迎页面的框架，官方的介绍是一个易于使用和定制的欢迎页面。现在在github上已经有1500多个star了，足以看出像我这样懒的程序员还是有不少的。<br>本文的核心内容也是介绍这个框架的使用了，大家英语还可以的话，可以直接去看原文。效果如下：<br><img src="https://imgconvert.csdnimg.cn/aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0ZXBoZW50dXNvL3dlbGNvbWUtYW5kcm9pZC9tYXN0ZXIvbWVkaWEvc2FtcGxlLXZpZGVvLmdpZg" alt="引导页面"></p>
<h2 id="特征"><a href="#特征" class="headerlink" title="特征"></a>特征</h2><p>完全可定制<br>RTL支持<br>能够使用内置布局或自定义片段<br>内置布局支持所有屏幕尺寸和方向</p>
<h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><h3 id="添加依赖"><a href="#添加依赖" class="headerlink" title="添加依赖"></a>添加依赖</h3><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">compile</span> <span class="string">&#x27;com.stephentuso:welcome:1.4.1&#x27;</span></span><br></pre></td></tr></table></figure>
<h3 id="简单使用"><a href="#简单使用" class="headerlink" title="简单使用"></a>简单使用</h3><p>1.首先要创建一个继承自WelcomeActivity的Activity，然后在AndroidManifest中进行注册，需要注意的是，必须使用WelcomeScreenTheme或其子主题：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">&quot;.activity.MyWelcomeActivity&quot;</span></span></span><br><span class="line"><span class="tag">          <span class="attr">android:theme</span> = <span class="string">&quot;@style/WelcomeScreenTheme&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>
<p>2.重写该Activity的configuration()方法。使用WelcomeConfiguration.Builder进行设置：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> WelcomeConfiguration <span class="title">configuration</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> WelcomeConfiguration.Builder(<span class="keyword">this</span>)</span><br><span class="line">            .defaultBackgroundColor(R.color.background)</span><br><span class="line">            .page(<span class="keyword">new</span> TitlePage(R.drawable.logo,</span><br><span class="line">                    <span class="string">&quot;Title&quot;</span>)</span><br><span class="line">            )</span><br><span class="line">            .page(<span class="keyword">new</span> BasicPage(R.drawable.image,</span><br><span class="line">                    <span class="string">&quot;Header&quot;</span>,</span><br><span class="line">                    <span class="string">&quot;More text.&quot;</span>)</span><br><span class="line">                    .background(R.color.red_background)</span><br><span class="line">            )</span><br><span class="line">            .page(<span class="keyword">new</span> BasicPage(R.drawable.image,</span><br><span class="line">                    <span class="string">&quot;Lorem ipsum&quot;</span>,</span><br><span class="line">                    <span class="string">&quot;dolor sit amet.&quot;</span>)</span><br><span class="line">            )</span><br><span class="line">            .swipeToDismiss(<span class="keyword">true</span>)</span><br><span class="line">            .build();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>不需要重写onCreate或调用setContentView方法。</p>
<p>3.欢迎屏幕通过WelcomeHelper来进行展示。onSaveInstanceState需要确保只有一个欢迎屏幕的实例启动。将以下内容添加到要显示欢迎屏幕的活动（可能是您的启动器活动）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">WelcomeHelper welcomeScreen;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    welcomeScreen = <span class="keyword">new</span> WelcomeHelper(<span class="keyword">this</span>, MyWelcomeActivity.class);</span><br><span class="line">    welcomeScreen.show(savedInstanceState);</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onSaveInstanceState</span><span class="params">(Bundle outState)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>.onSaveInstanceState(outState);</span><br><span class="line">    welcomeScreen.onSaveInstanceState(outState);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>至此，该框架在我们的校园APP中已经应用上了，大家如果对效果不满意的话，还可以进行更多定制。具体用法，就要详见官方文档了哈！</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E5%AE%89%E5%8D%93%E7%9A%84%E5%8F%8C%E5%B1%8F%E5%BC%82%E6%98%BE/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E5%AE%89%E5%8D%93%E7%9A%84%E5%8F%8C%E5%B1%8F%E5%BC%82%E6%98%BE/" class="post-title-link" itemprop="url">安卓的双屏异显</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>参考文章：<br>布列瑟农的秋天    <a target="_blank" rel="noopener" href="https://blog.csdn.net/wlwl0071986/article/details/48542923">https://blog.csdn.net/wlwl0071986/article/details/48542923</a><br>后时代的觉悟   <a target="_blank" rel="noopener" href="https://blog.csdn.net/liqianwei1230/article/details/78606935">https://blog.csdn.net/liqianwei1230/article/details/78606935</a><br>明朗晨光     Android | 说说Presentation    <a target="_blank" rel="noopener" href="https://blog.csdn.net/u011386173/article/details/88717174">https://blog.csdn.net/u011386173/article/details/88717174</a></p>
<h1 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h1><p>在做项目时，有一个双屏显示的需求（类似饭店里面点餐的那种台式安卓机，两面屏幕）。百度一番，发现了 presentation 关键词是实现双屏异显功能的重点。在此记录一下笔记，供后续参考。</p>
<h1 id="官方释义"><a href="#官方释义" class="headerlink" title="官方释义"></a>官方释义</h1><p>如果你英语能力可以的话，官方文档请直接食用：</p>
<h2 id="官方文档"><a href="#官方文档" class="headerlink" title="官方文档"></a>官方文档</h2><blockquote>
<p>A presentation is a special kind of dialog whose purpose is to present content on a secondary display. A Presentation is associated with the target Display at creation time and configures its context and resource configuration according to the display’s metrics.</p>
</blockquote>
<blockquote>
<p>Notably, the Context of a presentation is different from the context of its containing Activity. It is important to inflate the layout of a presentation and load other resources using the presentation’s own context to ensure that assets of the correct size and density for the target display are loaded.</p>
</blockquote>
<blockquote>
<p>A presentation is automatically canceled (see cancel()) when the display to which it is attached is removed. An activity should take care of pausing and resuming whatever content is playing within the presentation whenever the activity itself is paused or resumed.</p>
</blockquote>
<h2 id="我的翻译"><a href="#我的翻译" class="headerlink" title="我的翻译"></a>我的翻译</h2><p>presentation 是一个特殊的 dialog ，主要的目的是在辅助显示屏上显示内容，Presentation 在创建的时候需要和特定的 Display 相关联。 </p>
<p>值得注意的是，presentation 的上下文与包含它的Activity 的上下文不同。重要的是要填充 presentation 的布局，并使用表示本身的上下文加载其他资源，以确保加载了用于目标显示的正确大小和密度的资源。</p>
<p>当presentation 关联的屏幕被移除后，presentation会自动被cancel，当Activity本身pause和resume的时候，Activity应该负责暂停和恢复presentation中的播放的任何内容。</p>
<h2 id="解析总结"><a href="#解析总结" class="headerlink" title="解析总结"></a>解析总结</h2><p>把官方文档总结整理一下，可以得出以下几点结论：</p>
<ul>
<li>presentation 父类是 dialog，所以和dialog具有相似的属性和方法。</li>
<li>presentation 的容器是屏幕，创建时即需指定。屏幕移除，则presentation自动取消。</li>
<li>presentation 的上下文与包含它的Activity 的上下文不同。</li>
</ul>
<h1 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h1><p>demo 示例</p>
<ol>
<li>设置权限<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- 显示系统窗口权限 --&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">&quot;android.permission.SYSTEM_ALERT_WINDOW&quot;</span>/&gt;</span></span><br><span class="line"> <span class="comment">&lt;!-- 在 屏幕最顶部显示addview--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">&quot;android.permission.SYSTEM_OVERLAY_WINDOW&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure></li>
<li>自定义类 继承Presentation</li>
</ol>
<p>辅助显示屏的内容：</p>
<ul>
<li>需要扩展 Presentation 类，并实现 onCreate() 回调方法。在 onCreate() 中，调用setContentView() 来指定您要在辅助显示屏上显示的 UI。 </li>
<li>作为 Dialog 类的扩展，Presentation 类提供了一个区域，其中可以在辅助显示屏上显示不同的 UI。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DifferentDislay</span> <span class="keyword">extends</span> <span class="title">Presentation</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">       <span class="function"><span class="keyword">public</span> <span class="title">DifferentDislay</span><span class="params">(Context outerContext, Display display)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">super</span>(outerContext,display);</span><br><span class="line"></span><br><span class="line">       &#125;</span><br><span class="line">       <span class="meta">@Override</span></span><br><span class="line">       <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">           setContentView(R.layout.second_screen);</span><br><span class="line"></span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<p>3.代码实现：<br>根据官网文档，获取显示屏的代码如下;</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);</span><br><span class="line">Display[] presentationDisplays = displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);</span><br><span class="line"><span class="keyword">if</span> (presentationDisplays.length &gt; <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="comment">// If there is more than one suitable presentation display, then we could consider</span></span><br><span class="line">    <span class="comment">// giving the user a choice.  For this example, we simply choose the first display</span></span><br><span class="line">    <span class="comment">// which is the one the system recommends as the preferred presentation display.</span></span><br><span class="line">    Display display = presentationDisplays[<span class="number">0</span>];</span><br><span class="line">    Presentation presentation = <span class="keyword">new</span> MyPresentation(context, presentationDisplay);</span><br><span class="line">    presentation.show();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> DifferentDislay mPresentation;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        DisplayManager manager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);</span><br><span class="line">        Display[] displays = manager.getDisplays();</span><br><span class="line">        <span class="comment">// displays[0] 主屏</span></span><br><span class="line">        <span class="comment">// displays[1] 副屏</span></span><br><span class="line">        DifferentDislay differentDislay = <span class="keyword">new</span> DifferentDislay(<span class="keyword">this</span>,displays[<span class="number">1</span>]);</span><br><span class="line">        differentDislay.getWindow().setType(</span><br><span class="line">                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);</span><br><span class="line">        differentDislay.show();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DifferentDislay</span> <span class="keyword">extends</span> <span class="title">Presentation</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="title">DifferentDislay</span><span class="params">(Context outerContext, Display display)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">super</span>(outerContext,display);</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">            <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">            setContentView(R.layout.second_screen);</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在辅助显示屏上播放视频：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> <span class="keyword">implements</span> <span class="title">MediaPlayer</span>.<span class="title">OnPreparedListener</span>, <span class="title">SurfaceHolder</span>.<span class="title">Callback</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">&quot;Bradlley&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> DifferentDislay mPresentation;</span><br><span class="line">    <span class="keyword">private</span> MediaPlayer mMediaPlayer;</span><br><span class="line">    <span class="keyword">private</span> SurfaceView mSurfaceView;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.activity_main);</span><br><span class="line"></span><br><span class="line">        DisplayManager manager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);</span><br><span class="line">        Display[] displays = manager.getDisplays();</span><br><span class="line">        <span class="comment">// displays[0] 主屏</span></span><br><span class="line">        <span class="comment">// displays[1] 副屏</span></span><br><span class="line">        DifferentDislay differentDislay = <span class="keyword">new</span> DifferentDislay(<span class="keyword">this</span>,displays[<span class="number">1</span>]);</span><br><span class="line">        differentDislay.getWindow().setType(</span><br><span class="line">                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);</span><br><span class="line">        differentDislay.show();</span><br><span class="line"></span><br><span class="line">        mMediaPlayer = <span class="keyword">new</span> MediaPlayer();</span><br><span class="line">        mMediaPlayer.setOnPreparedListener(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line">        mSurfaceView= differentDislay.getSurfaceView();</span><br><span class="line">        mSurfaceView.getHolder().addCallback(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">surfaceCreated</span><span class="params">(SurfaceHolder holder)</span> </span>&#123;</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;surfaceCreated: &quot;</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            mMediaPlayer.setDataSource(<span class="string">&quot;/sdcard/Movies/mv.wmv&quot;</span>);</span><br><span class="line">            mMediaPlayer.setDisplay(mSurfaceView.getHolder());</span><br><span class="line">            mMediaPlayer.prepareAsync();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">surfaceChanged</span><span class="params">(SurfaceHolder holder, <span class="keyword">int</span> format, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">surfaceDestroyed</span><span class="params">(SurfaceHolder holder)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onPrepared</span><span class="params">(MediaPlayer mp)</span> </span>&#123;</span><br><span class="line">        mMediaPlayer.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>DifferentDislay 代码片段：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DifferentDislay</span> <span class="keyword">extends</span> <span class="title">Presentation</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String TAG = <span class="string">&quot;DifferentDislay&quot;</span>;</span><br><span class="line">    <span class="keyword">private</span> SurfaceView mSurfaceView;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DifferentDislay</span><span class="params">(Context outerContext, Display display)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(outerContext,display);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        Log.d(TAG, <span class="string">&quot;onCreate: &quot;</span>);</span><br><span class="line">        setContentView(R.layout.second_screen);</span><br><span class="line">        mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> SurfaceView <span class="title">getSurfaceView</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> mSurfaceView;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="原理探究"><a href="#原理探究" class="headerlink" title="原理探究"></a>原理探究</h1><h2 id="presentation-处在哪个View层级？"><a href="#presentation-处在哪个View层级？" class="headerlink" title="presentation 处在哪个View层级？"></a>presentation 处在哪个View层级？</h2><p>presentation 布局</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">PhoneWindows $ DecorView</span><br><span class="line">----LinearLayout</span><br><span class="line">--------Iaction_mode bar_stub（ViewStub）</span><br><span class="line">--------content（FrameLayout）</span><br><span class="line">------------LinearLayout（presentation 布局的根节点）</span><br></pre></td></tr></table></figure>
<p>Dialog布局</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">PhoneWindows $ DecorView</span><br><span class="line">---- FrameLayout</span><br><span class="line">---- ---- content（FrameLayout）</span><br><span class="line">---- ---- ---- LinearLayout（presentation 布局的根节点）</span><br><span class="line">---- ---- Iaction_mode bar_stub（ViewStub）</span><br></pre></td></tr></table></figure>
<p>由此可见，presentation和dialog布局层级是一样的</p>
<h1 id="琐碎知识"><a href="#琐碎知识" class="headerlink" title="琐碎知识"></a>琐碎知识</h1><p>1.如何获取设备上的屏幕？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">DisplayManager  mDisplayManager;<span class="comment">//屏幕管理类</span></span><br><span class="line">Display[]  displays;<span class="comment">//屏幕数组</span></span><br><span class="line">mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);</span><br><span class="line">displays =mDisplayManager.getDisplays();</span><br></pre></td></tr></table></figure>

<p>2.主屏和副屏的区分？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">主屏：displays[<span class="number">0</span>]</span><br><span class="line">副屏：displays[<span class="number">1</span>]</span><br></pre></td></tr></table></figure>

<p>3.如何在副屏上展示内容？</p>
<p>通过Presentation来实现，Presentation继承了Dialog。假设我们写了一个DifferentDislay的类，这个类是要继承<br>Presentation类的。代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">privateclass DifferentDislay extends Presentation&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DifferentDislay</span><span class="params">(ContextouterContext, Display display)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(outerContext,display);</span><br><span class="line">        <span class="comment">//TODOAuto-generated constructor stub  </span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function">protectedvoid <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line">        setContentView(R.layout.layout02);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>4.开启副屏</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">DifferentDislay  mPresentation =<span class="keyword">new</span> DifferentDislay (getApplicationContext(),displays[<span class="number">1</span>]);<span class="comment">//displays[1]是副屏</span></span><br><span class="line">mPresentation.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);</span><br><span class="line">mPresentation.show();</span><br></pre></td></tr></table></figure>









      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E5%AE%89%E5%8D%93%E6%89%AB%E7%A0%81%E6%9E%AA%E5%BC%80%E5%8F%91%EF%BC%8C%E6%8B%A6%E6%88%AA%E6%89%AB%E7%A0%81%E4%BA%8B%E4%BB%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E5%AE%89%E5%8D%93%E6%89%AB%E7%A0%81%E6%9E%AA%E5%BC%80%E5%8F%91%EF%BC%8C%E6%8B%A6%E6%88%AA%E6%89%AB%E7%A0%81%E4%BA%8B%E4%BB%B6/" class="post-title-link" itemprop="url">安卓扫码枪开发，拦截扫码事件</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><p>普通的手机开发，可以做一个二维码扫码的页面，用zxing一类的可以轻松实现，但是，如果是大型的安卓系统的终端机的话，摄像头不方便使用。可以依靠扫码枪来实现类似的效果。</p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>大型安卓设备与安卓手机的开发区别还是比较大的，尤其是扫码枪部分，有以下特点。例如：</p>
<ul>
<li>终端机的开发中，经常是Activity 数量比较少，Fragment比较多。</li>
<li>扫码枪需要全局响应。并不只局限于某一个Fragment。</li>
<li>大多数扫码枪的原理是模拟键盘进行输入，所以不支持包含有汉字的二维码</li>
</ul>
<p>我这边使用是USB HID的扫码枪，即插即用，只需要在界面上有带有焦点的EditText ，就可以获取扫码枪扫描的内容。大多数扫码枪是模拟键盘进行输入，大多数时候需要使用获取焦点的 EditText 接收其扫描的信息。<br>显然这并不能满足我们的需求。那么，仔细想一下，我们可以认为是在短时间内，识别了二维码中的非汉字信息，并依次按键。我们可以在按键的时候进行按键事件 onKeyDown 的拦截。等全部扫描完毕之后，再去执行其它操作。</p>
<blockquote>
<p>USB HID扫码枪会将扫描出来的内容转化为键盘事件，对应的就是Android中的KeyEvent事件，所以我们只需要在我们的activity中重写onKeyDown方法，即可获取相应事件。</p>
</blockquote>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><h3 id="找规律"><a href="#找规律" class="headerlink" title="找规律"></a>找规律</h3><p>既然我们要进行按键事件拦截，那么就得有一个明确的拦截开始时间和结束时间。可以先通过打印所有的keycode，来观察扫码后都执行了哪些按键。<br>例如我的扫码枪，在真正内容开始前和结束后，会按下一个keycode 值为 143 的按键。即 KEYCODE_NUM_LOCK    小键盘锁    。<br>那么可以设置一个布尔值，标识当前是否是扫描状态。并在结束的时候进行下一步操作。即：</p>
<blockquote>
<p>怎么进行下一步操作？方法太多了，EventBus，接口回调，方法，……</p>
</blockquote>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> isScan = <span class="literal">false</span>;  <span class="comment">// 当前是否是扫描状态</span></span><br><span class="line">···</span><br><span class="line"><span class="keyword">if</span> (keyCode == KeyEvent.KEYCODE_NUM_LOCK) &#123;</span><br><span class="line">          <span class="keyword">if</span> (isScan) &#123;</span><br><span class="line">              LogUtils.e(<span class="string">&quot;String ==&gt;&quot;</span> + ScanString)</span><br><span class="line">              ScanString = <span class="string">&quot;&quot;</span></span><br><span class="line">          &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">              LogUtils.e(<span class="string">&quot;String ==&gt;&quot;</span> + <span class="string">&quot;开始扫描……&quot;</span>)</span><br><span class="line">          &#125;</span><br><span class="line">          isScan = !isScan</span><br><span class="line">          <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (isScan) &#123;</span><br><span class="line">          ScanString += getInputCode(event).toString()</span><br><span class="line">          <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">      &#125;</span><br></pre></td></tr></table></figure>
<h3 id="拼接字符串"><a href="#拼接字符串" class="headerlink" title="拼接字符串"></a>拼接字符串</h3><p>只获取到了 keycode 值不行，还需要将keycode值转为具体的内容。</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获取扫描内容</span></span><br><span class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">fun</span> <span class="title">getInputCode</span><span class="params">(event: <span class="type">KeyEvent</span>)</span></span>: <span class="built_in">Char</span> &#123;</span><br><span class="line">    <span class="keyword">val</span> keyCode = event.keyCode</span><br><span class="line">    <span class="keyword">val</span> aChar: <span class="built_in">Char</span></span><br><span class="line">    <span class="keyword">if</span> (keyCode &gt;= KeyEvent.KEYCODE_A &amp;&amp; keyCode &lt;= KeyEvent.KEYCODE_Z) &#123;</span><br><span class="line">        <span class="comment">//字母</span></span><br><span class="line">        aChar = ((<span class="string">&#x27;a&#x27;</span>).toInt() + keyCode - KeyEvent.KEYCODE_A).toChar()</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (keyCode &gt;= KeyEvent.KEYCODE_0 &amp;&amp; keyCode &lt;= KeyEvent.KEYCODE_9) &#123;</span><br><span class="line">        <span class="comment">//数字</span></span><br><span class="line">        aChar = (<span class="string">&#x27;0&#x27;</span> + keyCode - KeyEvent.KEYCODE_0).toChar()</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (keyCode == KeyEvent.KEYCODE_ENTER) &#123;</span><br><span class="line">        aChar = <span class="number">0.</span>toChar()</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//其他符号</span></span><br><span class="line">        aChar = event.unicodeChar.toChar()</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> aChar</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h2><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> isScan = <span class="literal">false</span>;  <span class="comment">// 当前是否是扫描状态</span></span><br><span class="line"><span class="keyword">var</span> ScanString = <span class="string">&quot;&quot;</span>;  <span class="comment">// 扫描出的内容</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">override</span> <span class="function"><span class="keyword">fun</span> <span class="title">onKeyDown</span><span class="params">(keyCode: <span class="type">Int</span>, event: <span class="type">KeyEvent</span>)</span></span>: <span class="built_in">Boolean</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (keyCode == KeyEvent.KEYCODE_NUM_LOCK) &#123;</span><br><span class="line">        <span class="keyword">if</span> (isScan) &#123;</span><br><span class="line">            LogUtils.e(<span class="string">&quot;String ==&gt;&quot;</span> + ScanString)</span><br><span class="line">            ScanString = <span class="string">&quot;&quot;</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LogUtils.e(<span class="string">&quot;String ==&gt;&quot;</span> + <span class="string">&quot;开始扫描……&quot;</span>)</span><br><span class="line">        &#125;</span><br><span class="line">        isScan = !isScan</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (isScan) &#123;</span><br><span class="line">        ScanString += getInputCode(event).toString()</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.onKeyDown(keyCode, event)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取扫描内容</span></span><br><span class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">fun</span> <span class="title">getInputCode</span><span class="params">(event: <span class="type">KeyEvent</span>)</span></span>: <span class="built_in">Char</span> &#123;</span><br><span class="line">    <span class="keyword">val</span> keyCode = event.keyCode</span><br><span class="line">    <span class="keyword">val</span> aChar: <span class="built_in">Char</span></span><br><span class="line">    <span class="keyword">if</span> (keyCode &gt;= KeyEvent.KEYCODE_A &amp;&amp; keyCode &lt;= KeyEvent.KEYCODE_Z) &#123;</span><br><span class="line">        <span class="comment">//字母</span></span><br><span class="line">        aChar = ((<span class="string">&#x27;a&#x27;</span>).toInt() + keyCode - KeyEvent.KEYCODE_A).toChar()</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (keyCode &gt;= KeyEvent.KEYCODE_0 &amp;&amp; keyCode &lt;= KeyEvent.KEYCODE_9) &#123;</span><br><span class="line">        <span class="comment">//数字</span></span><br><span class="line">        aChar = (<span class="string">&#x27;0&#x27;</span> + keyCode - KeyEvent.KEYCODE_0).toChar()</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (keyCode == KeyEvent.KEYCODE_ENTER) &#123;</span><br><span class="line">        aChar = <span class="number">0.</span>toChar()</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//其他符号</span></span><br><span class="line">        aChar = event.unicodeChar.toChar()</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> aChar</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="安卓keycode列表"><a href="#安卓keycode列表" class="headerlink" title="安卓keycode列表"></a>安卓keycode列表</h2><p>详细列表 见  <a target="_blank" rel="noopener" href="https://blog.csdn.net/feizhixuan46789/article/details/16801429">https://blog.csdn.net/feizhixuan46789/article/details/16801429</a><br>此处简单总结一下keycode列表。</p>
<ul>
<li>0-9 对应的是 KEYCODE_0 - KEYCODE_9，真实的值为 7 - 16</li>
<li>A-Z 对应的是 KEYCODE_A - KEYCODE_Z ，值为 29 - 54</li>
<li>上下左右中  对应 19-23，</li>
<li>功能键如下：<br>KEYCODE_ENTER    回车键    66<br>KEYCODE_ESCAPE    ESC键    111<br>KEYCODE_DPAD_CENTER    导航键 确定键    23<br>KEYCODE_DPAD_UP    导航键 向上    19<br>KEYCODE_DPAD_DOWN    导航键 向下    20<br>KEYCODE_DPAD_LEFT    导航键 向左    21<br>KEYCODE_DPAD_RIGHT    导航键 向右    22<br>KEYCODE_MOVE_HOME    光标移动到开始键    122<br>KEYCODE_MOVE_END    光标移动到末尾键    123<br>KEYCODE_PAGE_UP    向上翻页键    92<br>KEYCODE_PAGE_DOWN    向下翻页键    93<br>KEYCODE_DEL    退格键    67<br>KEYCODE_FORWARD_DEL    删除键    112<br>KEYCODE_INSERT    插入键    124<br>KEYCODE_TAB    Tab键    61<br>KEYCODE_NUM_LOCK    小键盘锁    143<br>KEYCODE_CAPS_LOCK    大写锁定键    115<br>KEYCODE_BREAK    Break/Pause键    121<br>KEYCODE_SCROLL_LOCK    滚动锁定键    116<br>KEYCODE_ZOOM_IN    放大键    168<br>KEYCODE_ZOOM_OUT    缩小键    169</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E5%AE%89%E5%8D%93%E6%88%AA%E5%B1%8F%E7%9B%91%E5%90%AC/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E5%AE%89%E5%8D%93%E6%88%AA%E5%B1%8F%E7%9B%91%E5%90%AC/" class="post-title-link" itemprop="url">详细讲解安卓截屏监听操作</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><p>项目中有时会遇到监听用户截屏操作的需求，应用场景也比较丰富。</p>
<p>eg. 1: 某个隐私页面禁止截屏，当发现用户截屏后，删除该截屏文件；</p>
<p>eg. 2: 当用户截屏时，可能是需要进行分享，意见反馈等操作，此时进行弹窗提示，方便用户操作（支付宝就有类似功能）。</p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>安卓系统里面并没有提供监听截屏的接口，所以就需要自己去实现（不然也不会有本文）。</p>
<ul>
<li><p>一般来说，对于某一操作的监听，第一直觉就是监听对应按键的点击事件。但是各种定制系统的截图方式五花八门，对截图按键的监听并不容易。</p>
<blockquote>
<p> 截图方式 ：</p>
<p>eg.1  快捷组合键截屏 ：【电源键】+【音量-】；【home键】+【音量-】；【电源键】+【Home键】……</p>
<p>eg.2 快捷菜单的截屏按键</p>
<p>eg.3 借助其它软件截屏：例如QQ辅助功能中的摇一摇截屏；豌豆荚，360手机助手等通过usb调试的截屏 ……</p>
<p>eg.4 定制系统的其它截屏方式：三指下滑截屏 ；长按指纹键截屏 ……</p>
</blockquote>
<p>综上，此路不通。</p>
</li>
<li><p>既然无法通过按键点击判断出截屏操作，那就只好在生成截屏图片后进行判断。</p>
<p>也就是<strong>【对图库进行监听】</strong>，如果 <strong>【特定时间】</strong>增加了 **【符合规则的图片】 **，则判定用户进行了截图操作。</p>
</li>
</ul>
<h2 id="具体操作"><a href="#具体操作" class="headerlink" title="具体操作"></a>具体操作</h2><h3 id="1-权限"><a href="#1-权限" class="headerlink" title="1. 权限"></a>1. 权限</h3><p>因为对图库文件进行监听，需要用到内存读的权限，所以需要在 AndroidManifest.xml 中添加权限:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">&quot;android.permission.READ_EXTERNAL_STORAGE&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>
<p>内存读写权限属于高危权限，在安卓6.0及以上的系统还需要进行动态权限的申请。在对应页面的Activity中进行权限判断以及申请。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (Build.VERSION.SDK_INT &gt; <span class="number">22</span>) &#123;</span><br><span class="line">    List&lt;String&gt; permissionList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">    <span class="comment">// 检查是否有内存权限</span></span><br><span class="line">    <span class="keyword">if</span> (ContextCompat.checkSelfPermission(ScreenshotsActivity.<span class="keyword">this</span>, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) &#123;</span><br><span class="line">        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="comment">// 开始截图监听</span></span><br><span class="line">        doStart();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (permissionList != <span class="keyword">null</span> &amp;&amp; (permissionList.size() != <span class="number">0</span>)) &#123;</span><br><span class="line">        ActivityCompat.requestPermissions(ScreenshotsActivity.<span class="keyword">this</span>, permissionList.toArray(<span class="keyword">new</span> String[permissionList.size()]), <span class="number">0</span>);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-创建截图监听器"><a href="#2-创建截图监听器" class="headerlink" title="2. 创建截图监听器"></a>2. 创建截图监听器</h3><p>创建截图监听器文件，当判断用户进行截图操作之后，以接口回调的方式进行处理。</p>
<blockquote>
<p>那么如何判断进行了截图操作呢？这就又回归到分析中的结论：</p>
<p>​    【监听图库】，若 【特定时间内】增加【符合规则的图片】，则判定进行了截图操作。</p>
</blockquote>
<h4 id="2-1-监听图库"><a href="#2-1-监听图库" class="headerlink" title="2.1 监听图库"></a>2.1 监听图库</h4><p>我们要对图库进行监听，需要读取媒体数据库。那么需要监听读取的列如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 读取媒体数据库时需要读取的列</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] MEDIA_PROJECTIONS = &#123;</span><br><span class="line">    MediaStore.Images.ImageColumns.DATA,</span><br><span class="line">        MediaStore.Images.ImageColumns.DATE_TAKEN,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 读取媒体数据库时需要读取的列, 其中 WIDTH 和 HEIGHT 字段在 API 16 以后才有</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] MEDIA_PROJECTIONS_API_16 = &#123;</span><br><span class="line">    MediaStore.Images.ImageColumns.DATA,</span><br><span class="line">    MediaStore.Images.ImageColumns.DATE_TAKEN,</span><br><span class="line">    MediaStore.Images.ImageColumns.WIDTH,</span><br><span class="line">    MediaStore.Images.ImageColumns.HEIGHT,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p> 图库监听代码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line">Cursor cursor = <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 数据改变时查询数据库中最后加入的一条数据</span></span><br><span class="line">    cursor = mContext.getContentResolver().query(</span><br><span class="line">            contentUri,</span><br><span class="line">            Build.VERSION.SDK_INT &lt; <span class="number">16</span> ? MEDIA_PROJECTIONS : MEDIA_PROJECTIONS_API_16,</span><br><span class="line">            <span class="keyword">null</span>,</span><br><span class="line">            <span class="keyword">null</span>,</span><br><span class="line">            MediaStore.Images.ImageColumns.DATE_ADDED + <span class="string">&quot; desc limit 1&quot;</span></span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (cursor == <span class="keyword">null</span>) &#123;</span><br><span class="line">        LogUtils.e(<span class="string">&quot;Deviant logic.&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!cursor.moveToFirst()) &#123;</span><br><span class="line">        LogUtils.e(<span class="string">&quot;Cursor no data.&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取各列的索引</span></span><br><span class="line">    <span class="keyword">int</span> dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);</span><br><span class="line">    <span class="keyword">int</span> dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);</span><br><span class="line">    <span class="keyword">int</span> widthIndex = -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">int</span> heightIndex = -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= <span class="number">16</span>) &#123;</span><br><span class="line">        widthIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.WIDTH);</span><br><span class="line">        heightIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.HEIGHT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取行数据</span></span><br><span class="line">    String data = cursor.getString(dataIndex);</span><br><span class="line">    <span class="keyword">long</span> dateTaken = cursor.getLong(dateTakenIndex);</span><br><span class="line">    <span class="keyword">int</span> width = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">int</span> height = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (widthIndex &gt;= <span class="number">0</span> &amp;&amp; heightIndex &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">        width = cursor.getInt(widthIndex);</span><br><span class="line">        height = cursor.getInt(heightIndex);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// API 16 之前, 宽高要手动获取</span></span><br><span class="line">        Point size = getImageSize(data);</span><br><span class="line">        width = size.x;</span><br><span class="line">        height = size.y;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 处理获取到的第一行数据</span></span><br><span class="line">    handleMediaRowData(data, dateTaken, width, height);</span><br></pre></td></tr></table></figure>

<h4 id="2-2-特定时间内"><a href="#2-2-特定时间内" class="headerlink" title="2.2 特定时间内"></a>2.2 特定时间内</h4><p>所谓特定时间内，即为监听增加开始和结束的方法。可以通过两个方法进行时间的控制。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 启动监听</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startListen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    assertInMainThread();</span><br><span class="line"></span><br><span class="line">    sHasCallbackPaths.clear();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 记录开始监听的时间戳</span></span><br><span class="line">    mStartListenTime = System.currentTimeMillis();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建内容观察者</span></span><br><span class="line">    mInternalObserver = <span class="keyword">new</span> MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mUiHandler);</span><br><span class="line">    mExternalObserver = <span class="keyword">new</span> MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mUiHandler);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注册内容观察者</span></span><br><span class="line">    mContext.getContentResolver().registerContentObserver(</span><br><span class="line">            MediaStore.Images.Media.INTERNAL_CONTENT_URI,</span><br><span class="line">            <span class="keyword">false</span>,</span><br><span class="line">            mInternalObserver</span><br><span class="line">    );</span><br><span class="line">    mContext.getContentResolver().registerContentObserver(</span><br><span class="line">            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,</span><br><span class="line">            <span class="keyword">false</span>,</span><br><span class="line">            mExternalObserver</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 停止监听</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stopListen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    assertInMainThread();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注销内容观察者</span></span><br><span class="line">    <span class="keyword">if</span> (mInternalObserver != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          mContext.getContentResolver().unregisterContentObserver(mInternalObserver);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        mInternalObserver = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (mExternalObserver != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          mContext.getContentResolver().unregisterContentObserver(mExternalObserver);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        mExternalObserver = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 清空数据</span></span><br><span class="line">    mStartListenTime = <span class="number">0</span>;</span><br><span class="line">    sHasCallbackPaths.clear();</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="2-3-符合规则的图片"><a href="#2-3-符合规则的图片" class="headerlink" title="2.3 符合规则的图片"></a>2.3 符合规则的图片</h4><p>判断是否符合规则，首先要先定制一个规则。判断截图，一般可以从以下几个方面进行判断：</p>
<ul>
<li><p>时间判断：</p>
<ul>
<li><p>也就是我们2.2中所说的特定时间内，如果图片的创建时间不在其中，则不认定为截图；</p>
</li>
<li><p>为了提高容错性，也可以对时间限制进行适当放松；</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果加入数据库的时间在开始监听之前, 或者与当前时间相差大于10秒, 则认为当前没有截屏</span></span><br><span class="line"><span class="keyword">if</span> (dateTaken &lt; mStartListenTime || (System.currentTimeMillis() - dateTaken) &gt; <span class="number">10</span> * <span class="number">1000</span>) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>尺寸判断：</p>
<ul>
<li><p>屏幕是有固定分辨率的，一般来说，屏幕截图的分辨率也应该小于屏幕的分辨率；</p>
</li>
<li><p>但是如果是长截图，则图片的高度会超出屏幕；（是否判断长截图，根据自己需求制定）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 尺寸判断</span></span><br><span class="line"><span class="keyword">if</span> (sScreenRealSize != <span class="keyword">null</span>) &#123;</span><br><span class="line">    <span class="comment">// 如果图片尺寸超出屏幕, 则认为当前没有截屏（此处没有判断长截屏）</span></span><br><span class="line">    <span class="keyword">if</span> (!((width &lt;= sScreenRealSize.x &amp;&amp; height &lt;= sScreenRealSize.y)</span><br><span class="line">            || (height &lt;= sScreenRealSize.x &amp;&amp; width &lt;= sScreenRealSize.y))) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li><p>路径名称判断</p>
<ul>
<li><p>通过统计来看，截图文件的图片绝大多数的情况下都会包含screenshot或者类似的关键字。我们在这里进行图片路径的判断。</p>
</li>
<li><p>经过整理得出以下关键字，已经涵盖绝大多数情况的系统机型截图：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 此处忽略大小写，后续判断时，统一转换为小写即可</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] KEYWORDS = &#123;</span><br><span class="line">        <span class="string">&quot;screenshot&quot;</span>, <span class="string">&quot;screen_shot&quot;</span>, <span class="string">&quot;screen-shot&quot;</span>, <span class="string">&quot;screen shot&quot;</span>,</span><br><span class="line">        <span class="string">&quot;screencapture&quot;</span>, <span class="string">&quot;screen_capture&quot;</span>, <span class="string">&quot;screen-capture&quot;</span>, <span class="string">&quot;screen capture&quot;</span>,</span><br><span class="line">        <span class="string">&quot;screencap&quot;</span>, <span class="string">&quot;screen_cap&quot;</span>, <span class="string">&quot;screen-cap&quot;</span>, <span class="string">&quot;screen cap&quot;</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure></li>
<li><p>判断是否包含关键字</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 将路径转换为小写</span></span><br><span class="line">data = data.toLowerCase();</span><br><span class="line"><span class="comment">// 判断图片路径是否含有指定的关键字之一, 如果有, 则认为当前截屏了</span></span><br><span class="line"><span class="keyword">for</span> (String keyWork : KEYWORDS) &#123;</span><br><span class="line">    <span class="keyword">if</span> (data.contains(keyWork)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
</ul>
<h3 id="3-调用"><a href="#3-调用" class="headerlink" title="3. 调用"></a>3. 调用</h3><p>完成上述操作之后，即可在响应页面开始监听，并进行响应操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">manager = ScreenShotListenManager.newInstance(ScreenshotsActivity.<span class="keyword">this</span>);</span><br><span class="line">manager.setListener(</span><br><span class="line">        <span class="keyword">new</span> ScreenShotListenManager.OnScreenShotListener() &#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onShot</span><span class="params">(String imagePath)</span> </span>&#123;</span><br><span class="line">                <span class="comment">// do something            </span></span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>









<h2 id="完整代码："><a href="#完整代码：" class="headerlink" title="完整代码："></a>完整代码：</h2><p>ScreenShotListenManager.java</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 读取媒体数据库时需要读取的列</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] MEDIA_PROJECTIONS = &#123;</span><br><span class="line">        MediaStore.Images.ImageColumns.DATA,</span><br><span class="line">        MediaStore.Images.ImageColumns.DATE_TAKEN,</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 读取媒体数据库时需要读取的列, 其中 WIDTH 和 HEIGHT 字段在 API 16 以后才有</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] MEDIA_PROJECTIONS_API_16 = &#123;</span><br><span class="line">        MediaStore.Images.ImageColumns.DATA,</span><br><span class="line">        MediaStore.Images.ImageColumns.DATE_TAKEN,</span><br><span class="line">        MediaStore.Images.ImageColumns.WIDTH,</span><br><span class="line">        MediaStore.Images.ImageColumns.HEIGHT,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 截屏依据中的路径判断关键字</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> String[] KEYWORDS = &#123;</span><br><span class="line">        <span class="string">&quot;screenshot&quot;</span>, <span class="string">&quot;screen_shot&quot;</span>, <span class="string">&quot;screen-shot&quot;</span>, <span class="string">&quot;screen shot&quot;</span>,</span><br><span class="line">        <span class="string">&quot;screencapture&quot;</span>, <span class="string">&quot;screen_capture&quot;</span>, <span class="string">&quot;screen-capture&quot;</span>, <span class="string">&quot;screen capture&quot;</span>,</span><br><span class="line">        <span class="string">&quot;screencap&quot;</span>, <span class="string">&quot;screen_cap&quot;</span>, <span class="string">&quot;screen-cap&quot;</span>, <span class="string">&quot;screen cap&quot;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Point sScreenRealSize;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 已回调过的路径</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> List&lt;String&gt; sHasCallbackPaths = <span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> Context mContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> OnScreenShotListener mListener;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">long</span> mStartListenTime;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 内部存储器内容观察者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> MediaContentObserver mInternalObserver;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 外部存储器内容观察者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> MediaContentObserver mExternalObserver;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 运行在 UI 线程的 Handler, 用于运行监听器回调</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Handler mUiHandler = <span class="keyword">new</span> Handler(Looper.getMainLooper());</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">ScreenShotListenManager</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (context == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">&quot;The context must not be null.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    mContext = context;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取屏幕真实的分辨率</span></span><br><span class="line">    <span class="keyword">if</span> (sScreenRealSize == <span class="keyword">null</span>) &#123;</span><br><span class="line">        sScreenRealSize = getRealScreenSize();</span><br><span class="line">        <span class="keyword">if</span> (sScreenRealSize != <span class="keyword">null</span>) &#123;</span><br><span class="line">            LogUtils.e(<span class="string">&quot;Screen Real Size: &quot;</span> + sScreenRealSize.x + <span class="string">&quot; * &quot;</span> + sScreenRealSize.y);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LogUtils.e(<span class="string">&quot;Get screen real size failed.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ScreenShotListenManager <span class="title">newInstance</span><span class="params">(Context context)</span> </span>&#123;</span><br><span class="line">    assertInMainThread();</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> ScreenShotListenManager(context);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 启动监听</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startListen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    assertInMainThread();</span><br><span class="line"></span><br><span class="line">    sHasCallbackPaths.clear();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 记录开始监听的时间戳</span></span><br><span class="line">    mStartListenTime = System.currentTimeMillis();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建内容观察者</span></span><br><span class="line">    mInternalObserver = <span class="keyword">new</span> MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mUiHandler);</span><br><span class="line">    mExternalObserver = <span class="keyword">new</span> MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mUiHandler);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注册内容观察者</span></span><br><span class="line">    mContext.getContentResolver().registerContentObserver(</span><br><span class="line">            MediaStore.Images.Media.INTERNAL_CONTENT_URI,</span><br><span class="line">            <span class="keyword">false</span>,</span><br><span class="line">            mInternalObserver</span><br><span class="line">    );</span><br><span class="line">    mContext.getContentResolver().registerContentObserver(</span><br><span class="line">            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,</span><br><span class="line">            <span class="keyword">false</span>,</span><br><span class="line">            mExternalObserver</span><br><span class="line">    );</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 停止监听</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stopListen</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    assertInMainThread();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注销内容观察者</span></span><br><span class="line">    <span class="keyword">if</span> (mInternalObserver != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          mContext.getContentResolver().unregisterContentObserver(mInternalObserver);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        mInternalObserver = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (mExternalObserver != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          mContext.getContentResolver().unregisterContentObserver(mExternalObserver);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        mExternalObserver = <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 清空数据</span></span><br><span class="line">    mStartListenTime = <span class="number">0</span>;</span><br><span class="line">    sHasCallbackPaths.clear();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 处理媒体数据库的内容改变</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleMediaContentChange</span><span class="params">(Uri contentUri)</span> </span>&#123;</span><br><span class="line">    Cursor cursor = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 数据改变时查询数据库中最后加入的一条数据</span></span><br><span class="line">        cursor = mContext.getContentResolver().query(</span><br><span class="line">                contentUri,</span><br><span class="line">                Build.VERSION.SDK_INT &lt; <span class="number">16</span> ? MEDIA_PROJECTIONS : MEDIA_PROJECTIONS_API_16,</span><br><span class="line">                <span class="keyword">null</span>,</span><br><span class="line">                <span class="keyword">null</span>,</span><br><span class="line">                MediaStore.Images.ImageColumns.DATE_ADDED + <span class="string">&quot; desc limit 1&quot;</span></span><br><span class="line">        );</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (cursor == <span class="keyword">null</span>) &#123;</span><br><span class="line">            LogUtils.e(<span class="string">&quot;Deviant logic.&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!cursor.moveToFirst()) &#123;</span><br><span class="line">            LogUtils.e(<span class="string">&quot;Cursor no data.&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取各列的索引</span></span><br><span class="line">        <span class="keyword">int</span> dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);</span><br><span class="line">        <span class="keyword">int</span> dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);</span><br><span class="line">        <span class="keyword">int</span> widthIndex = -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">int</span> heightIndex = -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= <span class="number">16</span>) &#123;</span><br><span class="line">            widthIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.WIDTH);</span><br><span class="line">            heightIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.HEIGHT);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取行数据</span></span><br><span class="line">        String data = cursor.getString(dataIndex);</span><br><span class="line">        <span class="keyword">long</span> dateTaken = cursor.getLong(dateTakenIndex);</span><br><span class="line">        <span class="keyword">int</span> width = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">int</span> height = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (widthIndex &gt;= <span class="number">0</span> &amp;&amp; heightIndex &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            width = cursor.getInt(widthIndex);</span><br><span class="line">            height = cursor.getInt(heightIndex);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// API 16 之前, 宽高要手动获取</span></span><br><span class="line">            Point size = getImageSize(data);</span><br><span class="line">            width = size.x;</span><br><span class="line">            height = size.y;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 处理获取到的第一行数据</span></span><br><span class="line">        handleMediaRowData(data, dateTaken, width, height);</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (cursor != <span class="keyword">null</span> &amp;&amp; !cursor.isClosed()) &#123;</span><br><span class="line">            cursor.close();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> Point <span class="title">getImageSize</span><span class="params">(String imagePath)</span> </span>&#123;</span><br><span class="line">    BitmapFactory.Options options = <span class="keyword">new</span> BitmapFactory.Options();</span><br><span class="line">    options.inJustDecodeBounds = <span class="keyword">true</span>;</span><br><span class="line">    BitmapFactory.decodeFile(imagePath, options);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Point(options.outWidth, options.outHeight);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 处理获取到的一行数据</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleMediaRowData</span><span class="params">(String data, <span class="keyword">long</span> dateTaken, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (checkScreenShot(data, dateTaken, width, height)) &#123;</span><br><span class="line">        LogUtils.e(<span class="string">&quot;ScreenShot: path = &quot;</span> + data + <span class="string">&quot;; size = &quot;</span> + width + <span class="string">&quot; * &quot;</span> + height</span><br><span class="line">                + <span class="string">&quot;; date = &quot;</span> + dateTaken);</span><br><span class="line">        <span class="keyword">if</span> (mListener != <span class="keyword">null</span> &amp;&amp; !checkCallback(data)) &#123;</span><br><span class="line">            mListener.onShot(data);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 如果在观察区间媒体数据库有数据改变，又不符合截屏规则，则输出到 log 待分析</span></span><br><span class="line">        LogUtils.e(<span class="string">&quot;Media content changed, but not screenshot: path = &quot;</span> + data</span><br><span class="line">                + <span class="string">&quot;; size = &quot;</span> + width + <span class="string">&quot; * &quot;</span> + height + <span class="string">&quot;; date = &quot;</span> + dateTaken);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 判断指定的数据行是否符合截屏条件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">checkScreenShot</span><span class="params">(String data, <span class="keyword">long</span> dateTaken, <span class="keyword">int</span> width, <span class="keyword">int</span> height)</span> </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * 判断依据一: 时间判断</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">// 如果加入数据库的时间在开始监听之前, 或者与当前时间相差大于10秒, 则认为当前没有截屏</span></span><br><span class="line">    <span class="keyword">if</span> (dateTaken &lt; mStartListenTime || (System.currentTimeMillis() - dateTaken) &gt; <span class="number">10</span> * <span class="number">1000</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * 判断依据二: 尺寸判断</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">if</span> (sScreenRealSize != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果图片尺寸超出屏幕, 则认为当前没有截屏</span></span><br><span class="line">        <span class="keyword">if</span> (!((width &lt;= sScreenRealSize.x &amp;&amp; height &lt;= sScreenRealSize.y)</span><br><span class="line">                || (height &lt;= sScreenRealSize.x &amp;&amp; width &lt;= sScreenRealSize.y))) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * 判断依据三: 路径判断</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">if</span> (TextUtils.isEmpty(data)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    data = data.toLowerCase();</span><br><span class="line">    <span class="comment">// 判断图片路径是否含有指定的关键字之一, 如果有, 则认为当前截屏了</span></span><br><span class="line">    <span class="keyword">for</span> (String keyWork : KEYWORDS) &#123;</span><br><span class="line">        <span class="keyword">if</span> (data.contains(keyWork)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 判断是否已回调过, 某些手机ROM截屏一次会发出多次内容改变的通知; &lt;br/&gt;</span></span><br><span class="line"><span class="comment"> * 删除一个图片也会发通知, 同时防止删除图片时误将上一张符合截屏规则的图片当做是当前截屏.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">checkCallback</span><span class="params">(String imagePath)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (sHasCallbackPaths.contains(imagePath)) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 大概缓存15~20条记录便可</span></span><br><span class="line">    <span class="keyword">if</span> (sHasCallbackPaths.size() &gt;= <span class="number">20</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">            sHasCallbackPaths.remove(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    sHasCallbackPaths.add(imagePath);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取屏幕分辨率</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> Point <span class="title">getRealScreenSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Point screenSize = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        screenSize = <span class="keyword">new</span> Point();</span><br><span class="line">        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);</span><br><span class="line">        Display defaultDisplay = windowManager.getDefaultDisplay();</span><br><span class="line">        <span class="keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.JELLY_BEAN_MR1) &#123;</span><br><span class="line">            defaultDisplay.getRealSize(screenSize);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Method mGetRawW = Display.class.getMethod(<span class="string">&quot;getRawWidth&quot;</span>);</span><br><span class="line">                Method mGetRawH = Display.class.getMethod(<span class="string">&quot;getRawHeight&quot;</span>);</span><br><span class="line">                screenSize.set(</span><br><span class="line">                        (Integer) mGetRawW.invoke(defaultDisplay),</span><br><span class="line">                        (Integer) mGetRawH.invoke(defaultDisplay)</span><br><span class="line">                );</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                screenSize.set(defaultDisplay.getWidth(), defaultDisplay.getHeight());</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> screenSize;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 设置截屏监听器</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setListener</span><span class="params">(OnScreenShotListener listener)</span> </span>&#123;</span><br><span class="line">    mListener = listener;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">interface</span> <span class="title">OnScreenShotListener</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onShot</span><span class="params">(String imagePath)</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">assertInMainThread</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (Looper.myLooper() != Looper.getMainLooper()) &#123;</span><br><span class="line">        StackTraceElement[] elements = Thread.currentThread().getStackTrace();</span><br><span class="line">        String methodMsg = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (elements != <span class="keyword">null</span> &amp;&amp; elements.length &gt;= <span class="number">4</span>) &#123;</span><br><span class="line">            methodMsg = elements[<span class="number">3</span>].toString();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">&quot;Call the method must be in main thread: &quot;</span> + methodMsg);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 媒体内容观察者(观察媒体数据库的改变)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">MediaContentObserver</span> <span class="keyword">extends</span> <span class="title">ContentObserver</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Uri mContentUri;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">MediaContentObserver</span><span class="params">(Uri contentUri, Handler handler)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(handler);</span><br><span class="line">        mContentUri = contentUri;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onChange</span><span class="params">(<span class="keyword">boolean</span> selfChange)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onChange(selfChange);</span><br><span class="line">        handleMediaContentChange(mContentUri);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>3、 调用 </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">manager = ScreenShotListenManager.newInstance(ScreenshotsActivity.<span class="keyword">this</span>);</span><br><span class="line">manager.setListener(</span><br><span class="line">        <span class="keyword">new</span> ScreenShotListenManager.OnScreenShotListener() &#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onShot</span><span class="params">(String imagePath)</span> </span>&#123;</span><br><span class="line">                <span class="comment">// do something</span></span><br><span class="line">               </span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>

<p>参考：<br><a target="_blank" rel="noopener" href="https://blog.csdn.net/xietansheng/article/details/52692163">https://blog.csdn.net/xietansheng/article/details/52692163</a> </p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E5%AE%89%E5%8D%93%E5%B0%8F%E6%8A%80%E5%B7%A7%EF%BC%9A%E8%BD%AF%E4%BB%B6%E4%B8%AD%E6%98%BE%E7%A4%BA%E7%89%88%E6%9C%AC%E6%89%93%E5%8C%85%E6%97%B6%E9%97%B4/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E5%AE%89%E5%8D%93%E5%B0%8F%E6%8A%80%E5%B7%A7%EF%BC%9A%E8%BD%AF%E4%BB%B6%E4%B8%AD%E6%98%BE%E7%A4%BA%E7%89%88%E6%9C%AC%E6%89%93%E5%8C%85%E6%97%B6%E9%97%B4/" class="post-title-link" itemprop="url">安卓小技巧：软件中显示版本打包时间</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>在项目的调试阶段，经常会被项目的版本搞的头昏脑涨。昨天一个版本，今天一个版本，明天又一个版本，多人长期测试的情况下，很容易混乱。本文分享一个小技巧，在软件中显示版本打包时间，权当抛转引玉，供大家开阔思路之用。</p>
<ol>
<li> 定义获取时间方法<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">def</span> getDebugTime() &#123;</span><br><span class="line">    String now = <span class="keyword">new</span> Date().format(<span class="string">&quot;yyyy-MM-dd HH:mm&quot;</span>)</span><br><span class="line">    <span class="keyword">return</span> <span class="string">&quot;\&quot;$&#123;now&#125;\&quot;&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>定义变量，调用获取时间方法<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">android &#123;</span><br><span class="line">    signingConfigs &#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    defaultConfig &#123;</span><br><span class="line">        ...</span><br><span class="line">        buildConfigField <span class="string">&quot;String&quot;</span>, <span class="string">&quot;debugVersion&quot;</span>, getDebugTime()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li>可以在应用中使用变量，比如在登录页面进行显示</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">textView.setText(BuildConfig.debugVersion);</span><br></pre></td></tr></table></figure>
<ol start="4">
<li>也可以在打包出来的文件上带上版本信息，打包时间等。具体如下：</li>
</ol>
<figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">applicationVariants.all &#123;</span><br><span class="line">       variant -&gt;</span><br><span class="line">           variant.outputs.all &#123;</span><br><span class="line">               outputFileName = <span class="string">&quot;zd_$&#123;variant.productFlavors[0].name&#125;_v$&#123;variant.productFlavors[0].versionName&#125;-$&#123;releaseTime()&#125;.apk&quot;</span></span><br><span class="line">           &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E5%AE%89%E5%8D%93%E5%AF%BC%E5%87%BAExcel%EF%BC%8Ctxt%E6%96%87%E4%BB%B6%E5%B7%A5%E5%85%B7%E7%B1%BB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E5%AE%89%E5%8D%93%E5%AF%BC%E5%87%BAExcel%EF%BC%8Ctxt%E6%96%87%E4%BB%B6%E5%B7%A5%E5%85%B7%E7%B1%BB/" class="post-title-link" itemprop="url">安卓导出Excel，txt文件工具类</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>﻿## 开始</p>
<p>安卓开发中，有时候会遇到到处文件的需求，尤其是平板上的开发，这个需求更为普遍，本文记录导出excel，txt文件的方法，并提供工具类，抛砖引玉，让大家遇到类似需求的时候，处理起来更为顺手。<br>导出工具类。使用方法</p>
<h2 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h2><p>导出txt文件需求比较普遍。直接进行文件读写即可。导出excel文件往往需要借助第三方jar包的帮助完成。（下载链接后续补上）</p>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>需求中，往往遇到的情况是将一个列表的数据进行导出。因此本文处理的也是这种情况，直接传入列表即可。</p>
<p>工具类调用方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ExportUtil.writeExcel(DataExportActivity.<span class="keyword">this</span>, actionLogModels, <span class="string">&quot;excel&quot;</span>);</span><br></pre></td></tr></table></figure>
<p>本文以行为日志的记录为例，具体bean类型如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">  <span class="keyword">public</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">ActionLogModel</span> </span>&#123;</span><br><span class="line">      <span class="keyword">private</span> <span class="keyword">int</span> id;</span><br><span class="line">      <span class="keyword">private</span> String action_date;</span><br><span class="line">      <span class="keyword">private</span> String action_time;</span><br><span class="line">      <span class="keyword">private</span> String user_name;</span><br><span class="line">      <span class="keyword">private</span> String user_action;</span><br><span class="line"><span class="comment">// ... 省略 构造函数</span></span><br><span class="line"> 		<span class="comment">// ... 省略 get set 方法</span></span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>

<p>文件导出工具类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 导出工具类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ExportUtil</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 内存地址</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String root = Environment.getExternalStorageDirectory().getPath();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String saveDir =root+<span class="string">&quot;/export&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">writeExcel</span><span class="params">(Context context, List&lt;ActionLogModel&gt; actionLogModels, String fileName)</span> <span class="keyword">throws</span> Exception </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) &amp;&amp; getAvailableStorage() &gt; <span class="number">1000000</span>) &#123;</span><br><span class="line">            ToastUtil.showShort(<span class="string">&quot;SD卡不可用&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        File file;</span><br><span class="line">        File dir = <span class="keyword">new</span> File(saveDir);</span><br><span class="line">        <span class="keyword">if</span> (!dir.exists()) &#123;</span><br><span class="line">            dir.mkdirs();</span><br><span class="line">        &#125;</span><br><span class="line">        file = <span class="keyword">new</span> File(dir, fileName + <span class="string">&quot;.xls&quot;</span>);</span><br><span class="line">        String[] title = &#123;<span class="string">&quot;日志id&quot;</span>, <span class="string">&quot;日期&quot;</span>, <span class="string">&quot;时间&quot;</span>, <span class="string">&quot;用户&quot;</span>, <span class="string">&quot;操作&quot;</span>&#125;;</span><br><span class="line">        <span class="comment">// 创建Excel工作表</span></span><br><span class="line">        WritableWorkbook wwb;</span><br><span class="line">        OutputStream os = <span class="keyword">new</span> FileOutputStream(file);</span><br><span class="line">        wwb = Workbook.createWorkbook(os);</span><br><span class="line">        <span class="comment">// 添加第一个工作表并设置第一个Sheet的名字</span></span><br><span class="line">        WritableSheet sheet = wwb.createSheet(<span class="string">&quot;用户操作&quot;</span>, <span class="number">0</span>);</span><br><span class="line">        Label label;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; title.length; i++) &#123;</span><br><span class="line">            <span class="comment">// Label(x,y,z) 代表单元格的第x+1列，第y+1行, 内容z</span></span><br><span class="line">            <span class="comment">// 在Label对象的子对象中指明单元格的位置和内容</span></span><br><span class="line">            label = <span class="keyword">new</span> Label(i, <span class="number">0</span>, title[i], getHeader());</span><br><span class="line">            <span class="comment">// 将定义好的单元格添加到工作表中</span></span><br><span class="line">            sheet.addCell(label);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; actionLogModels.size(); i++) &#123;</span><br><span class="line">            ActionLogModel actionLogModel = actionLogModels.get(i);</span><br><span class="line"></span><br><span class="line">            Label id = <span class="keyword">new</span> Label(<span class="number">0</span>, i + <span class="number">1</span>, <span class="string">&quot;&quot;</span> + actionLogModel.getId());</span><br><span class="line">            Label date = <span class="keyword">new</span> Label(<span class="number">1</span>, i + <span class="number">1</span>, actionLogModel.getAction_date());</span><br><span class="line">            Label time = <span class="keyword">new</span> Label(<span class="number">2</span>, i + <span class="number">1</span>, actionLogModel.getAction_time());</span><br><span class="line">            Label user_name = <span class="keyword">new</span> Label(<span class="number">3</span>, i + <span class="number">1</span>, actionLogModel.getUser_name());</span><br><span class="line">            Label user_action = <span class="keyword">new</span> Label(<span class="number">4</span>, i + <span class="number">1</span>, actionLogModel.getUser_action());</span><br><span class="line"></span><br><span class="line">            sheet.addCell(id);</span><br><span class="line">            sheet.addCell(date);</span><br><span class="line">            sheet.addCell(time);</span><br><span class="line">            sheet.addCell(user_name);</span><br><span class="line">            sheet.addCell(user_action);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 写入数据</span></span><br><span class="line">        wwb.write();</span><br><span class="line">        <span class="comment">// 关闭文件</span></span><br><span class="line">        wwb.close();</span><br><span class="line">        ToastUtil.showShort(<span class="string">&quot;写入成功&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (file.exists()) &#123;</span><br><span class="line">            LogUtil.e(<span class="string">&quot;文件确实创建了&quot;</span> + file.getAbsolutePath());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            LogUtil.e(<span class="string">&quot;假的，文件就没有创建&quot;</span> + file.getAbsolutePath());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> WritableCellFormat <span class="title">getHeader</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        WritableFont font = <span class="keyword">new</span> WritableFont(WritableFont.TIMES, <span class="number">10</span>, WritableFont.BOLD);<span class="comment">// 定义字体</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            font.setColour(Colour.BLUE);<span class="comment">// 蓝色字体</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (WriteException e1) &#123;</span><br><span class="line">            e1.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        WritableCellFormat format = <span class="keyword">new</span> WritableCellFormat(font);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            format.setAlignment(jxl.format.Alignment.CENTRE);<span class="comment">// 左右居中</span></span><br><span class="line">            format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);<span class="comment">// 上下居中</span></span><br><span class="line">            <span class="comment">// format.setBorder(Border.ALL, BorderLineStyle.THIN,</span></span><br><span class="line">            <span class="comment">// Colour.BLACK);// 黑色边框</span></span><br><span class="line">            <span class="comment">// format.setBackground(Colour.YELLOW);// 黄色背景</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (WriteException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> format;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取SD可用容量</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">long</span> <span class="title">getAvailableStorage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        StatFs statFs = <span class="keyword">new</span> StatFs(root);</span><br><span class="line">        <span class="keyword">long</span> blockSize = statFs.getBlockSize();</span><br><span class="line">        <span class="keyword">long</span> availableBlocks = statFs.getAvailableBlocks();</span><br><span class="line">        <span class="keyword">long</span> availableSize = blockSize * availableBlocks;</span><br><span class="line">        <span class="comment">// Formatter.formatFileSize(context, availableSize);</span></span><br><span class="line">        <span class="keyword">return</span> availableSize;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//写入文件保存在根目录下面</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">writeTxtFile</span><span class="params">(Context context, List&lt;ActionLogModel&gt; list, String file_name)</span> </span>&#123;</span><br><span class="line">        File dir = <span class="keyword">new</span> File(saveDir);</span><br><span class="line">        <span class="keyword">if</span> (!dir.exists()) &#123;</span><br><span class="line">            dir.mkdirs();</span><br><span class="line">        &#125;</span><br><span class="line">        File file;</span><br><span class="line"><span class="comment">//        String filename = Environment.getExternalStorageDirectory() + &quot;/&quot; + file_name + &quot;.txt&quot;;</span></span><br><span class="line">        FileOutputStream outputStream;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            file = <span class="keyword">new</span> File(dir, file_name + <span class="string">&quot;.txt&quot;</span>);</span><br><span class="line">            outputStream = <span class="keyword">new</span> FileOutputStream(file);</span><br><span class="line"><span class="comment">//            outputStream = openFileOutput(filename, MODE_PRIVATE);</span></span><br><span class="line">            outputStream.write(<span class="string">&quot;日志id\t日期\t\t时间\t用户\t操作\r\n&quot;</span>.getBytes(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">            <span class="keyword">for</span> (ActionLogModel actionLogModel : list) &#123;</span><br><span class="line">                outputStream.write((actionLogModel.getId() + <span class="string">&quot;\t&quot;</span> + actionLogModel.getAction_date() + <span class="string">&quot;\t&quot;</span> + actionLogModel.getAction_time() + <span class="string">&quot;\t&quot;</span></span><br><span class="line">                        + actionLogModel.getUser_name() + <span class="string">&quot;\t&quot;</span> + actionLogModel.getUser_action() + <span class="string">&quot;\r\n&quot;</span>).getBytes(<span class="string">&quot;utf-8&quot;</span>));</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            LogUtil.e(<span class="string">&quot;path &quot;</span> + file.getAbsolutePath());</span><br><span class="line">            outputStream.close();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">            LogUtil.e(<span class="string">&quot;异常异常 &quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E5%AE%89%E5%8D%93%E5%8F%AF%E4%B8%8A%E4%B8%8B%E6%BB%91%E5%8A%A8%E6%94%B9%E5%8F%98%E6%95%B0%E5%80%BC%E7%9A%84%E6%8A%98%E7%BA%BF%E5%9B%BE%EF%BC%88%E5%9F%BA%E4%BA%8Ehellochart%EF%BC%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E5%AE%89%E5%8D%93%E5%8F%AF%E4%B8%8A%E4%B8%8B%E6%BB%91%E5%8A%A8%E6%94%B9%E5%8F%98%E6%95%B0%E5%80%BC%E7%9A%84%E6%8A%98%E7%BA%BF%E5%9B%BE%EF%BC%88%E5%9F%BA%E4%BA%8Ehellochart%EF%BC%89/" class="post-title-link" itemprop="url">安卓可上下滑动改变数值的折线图（基于hellochart）</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <p>﻿# 需求</p>
<p>接手的公司的一个项目，有一个需求是折线图可以通过上下滑动改变数值。原先的大佬自己从头写的，也能实现功能。大佬后来也把思路和代码分享出来了。大家有兴趣的可以看一下。<br><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/cdf55a956beb">手把手教你写一个可以上下滑动点改变值的安卓折线图</a><br> 因为后来需求有些变动，原来的代码改动起来略显吃力，于是自己又以hellocharts为基础重新封装了一个自定义VIew，以此来实现功能。使用起来也更简洁一些。</p>
<p><strong>需求明确：</strong></p>
<ul>
<li>多条折线图切换滑动</li>
<li>手动上下滑动改变数值</li>
</ul>
<h1 id="实现效果图"><a href="#实现效果图" class="headerlink" title="实现效果图"></a>实现效果图</h1><p><img src="https://img-blog.csdnimg.cn/20190525193108723.gif" alt="滑动折线图"></p>
<h1 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h1><p>现在最好用的图表库大概就是hellochart了。但是hellochart并没有提供滑动改变数值的接口以及相关内容。因此我们在hellochart的基础上进行功能扩展，通过自定义控件来实现如图效果。<br>首先需要确定用户点击的是哪一条线，哪一个点。然后对对应的数据进行操作。重新进行赋值改变图表显示。</p>
<h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><h2 id="尺寸计算"><a href="#尺寸计算" class="headerlink" title="尺寸计算"></a>尺寸计算</h2><p>首先计算控件宽高，因为要滑动，不宜进行1：1的计算，滑动难度高。因此乘以0.75，以方便响应用户手势操作。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();</span><br><span class="line"><span class="keyword">int</span> height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();</span><br><span class="line"></span><br><span class="line">hLength = width * <span class="number">0.75f</span>;          <span class="comment">// x轴长度</span></span><br><span class="line">vLength = height * <span class="number">0.75f</span>;         <span class="comment">// y轴长度</span></span><br></pre></td></tr></table></figure>
<h2 id="触摸-按下响应事件"><a href="#触摸-按下响应事件" class="headerlink" title="触摸 - 按下响应事件"></a>触摸 - 按下响应事件</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">lineChartData = vccharts.<span class="keyword">this</span>.getLineChartData();</span><br><span class="line"></span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; lineChartData.getLines().size(); i++) &#123;</span><br><span class="line">                    Line line = lineChartData.getLines().get(i);</span><br><span class="line">                    <span class="keyword">int</span> pointRadius = ChartUtils.dp2px(density, line.getPointRadius());</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; line.getValues().size(); j++) &#123;</span><br><span class="line">                        PointValue pointValue = line.getValues().get(j);</span><br><span class="line">                        <span class="keyword">final</span> <span class="keyword">float</span> rawValueX = vccharts.<span class="keyword">this</span>.getChartComputator().computeRawX(pointValue.getX());</span><br><span class="line">                        <span class="keyword">final</span> <span class="keyword">float</span> rawValueY = vccharts.<span class="keyword">this</span>.getChartComputator().computeRawY(pointValue.getY());</span><br><span class="line">                        <span class="keyword">if</span> (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) &#123;</span><br><span class="line">                            LogUtils.e(<span class="string">&quot;     xuanzhong   xxx &quot;</span> + pointValue.getX() + <span class="string">&quot;        yyy   &quot;</span> + pointValue.getY() + <span class="string">&quot;           &quot;</span>);</span><br><span class="line">                            vccharts.<span class="keyword">this</span>.lineIndex = i;</span><br><span class="line">                            vccharts.<span class="keyword">this</span>.pointIndex = j;</span><br><span class="line">                            isMoveChange = <span class="keyword">true</span>;</span><br><span class="line">                            <span class="keyword">for</span> (Line line1 : lineChartData.getLines()) &#123;</span><br><span class="line">                                line1.setStrokeWidth(<span class="number">2</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                            line.setStrokeWidth(<span class="number">4</span>);</span><br><span class="line">                            selectLineNumber = i;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br></pre></td></tr></table></figure>
<h2 id="触摸-移动响应事件"><a href="#触摸-移动响应事件" class="headerlink" title="触摸 - 移动响应事件"></a>触摸 - 移动响应事件</h2><p>移动的时候需要判断位移以及差值，重新计算比例，确定新数值，进而更新数据，刷新View显示。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">                    <span class="keyword">float</span> y_max = <span class="number">10</span>;</span><br><span class="line">                    <span class="keyword">float</span> y_min = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//                    如果没有选中任何点</span></span><br><span class="line">                    <span class="keyword">if</span> (pointIndex == -<span class="number">1</span> || lineIndex == -<span class="number">1</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(event);</span><br><span class="line">                    &#125;</span><br><span class="line">                    lineChartData = vccharts.<span class="keyword">this</span>.getLineChartData();</span><br><span class="line">                    <span class="keyword">float</span> y = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getY();</span><br><span class="line">                    <span class="keyword">float</span> x = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getX();</span><br><span class="line">                    <span class="keyword">float</span> y_new = y_max - coordinateConversionY(touchY);</span><br></pre></td></tr></table></figure>
<p>数值保护，防止超过数据边界</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (y_new &lt; y_pre) &#123;</span><br><span class="line">       y_new = y_pre;</span><br><span class="line"> &#125;</span><br><span class="line"><span class="keyword">if</span> (y_new &gt; y_after) &#123;</span><br><span class="line">       y_new = y_after;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (y_new &lt; y_min) &#123;</span><br><span class="line">        y_new = y_min;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">if</span> (y_new &gt; y_max) &#123;</span><br><span class="line">       y_new = y_max;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>重新设置数据，通知数据变化</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).set(x, y_new);</span><br><span class="line"><span class="comment">// 通知数据变化</span></span><br><span class="line"> postInvalidate();</span><br></pre></td></tr></table></figure>
<h2 id="触摸-抬起事件"><a href="#触摸-抬起事件" class="headerlink" title="触摸 - 抬起事件"></a>触摸 - 抬起事件</h2><p>抬起的时候，取消选择的线条的状态，并且计算最终数据，进行显示</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (vccharts.<span class="keyword">this</span>.pointIndex == -<span class="number">1</span> || vccharts.<span class="keyword">this</span>.lineIndex == -<span class="number">1</span>) &#123;</span><br><span class="line">                        isMoveChange = <span class="keyword">false</span>;</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//                    通知数据变化</span></span><br><span class="line">                    List&lt;PointValue&gt; values = lineChartData.getLines().get(lineIndex).getValues();</span><br><span class="line">                    <span class="keyword">float</span>[] changeData = <span class="keyword">new</span> <span class="keyword">float</span>[values.size()];</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; values.size(); i++) &#123;</span><br><span class="line">                        changeData[i] = values.get(i).getY();</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (vcListener != <span class="keyword">null</span>) &#123;</span><br><span class="line">                            vcListener.DataChange(lineIndex, <span class="keyword">new</span> ZuniData(changeData));</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    postInvalidate();</span><br><span class="line"></span><br><span class="line">                    isMoveChange = <span class="keyword">false</span>;</span><br><span class="line">                    vccharts.<span class="keyword">this</span>.pointIndex = -<span class="number">1</span>;</span><br><span class="line">                    vccharts.<span class="keyword">this</span>.lineIndex = -<span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br></pre></td></tr></table></figure>
<h2 id="判断是否点击对应点"><a href="#判断是否点击对应点" class="headerlink" title="判断是否点击对应点"></a>判断是否点击对应点</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isInArea</span><span class="params">(<span class="keyword">float</span> x, <span class="keyword">float</span> y, <span class="keyword">float</span> touchX, <span class="keyword">float</span> touchY, <span class="keyword">float</span> radius)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">float</span> diffX = touchX - x;</span><br><span class="line">    <span class="keyword">float</span> diffY = touchY - y;</span><br><span class="line">    <span class="keyword">return</span> Math.pow(diffX, <span class="number">2</span>) + Math.pow(diffY, <span class="number">2</span>) &lt;= <span class="number">2</span> * Math.pow(radius, <span class="number">2</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<h1 id="完整代码："><a href="#完整代码：" class="headerlink" title="完整代码："></a>完整代码：</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">vccharts</span> <span class="keyword">extends</span> <span class="title">LineChartView</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_TOUCH_TOLERANCE_MARGIN_DP = <span class="number">4</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ChartTouchHandler0 touchHandler0;</span><br><span class="line">    <span class="keyword">boolean</span> isMoveChange = <span class="keyword">false</span>; <span class="comment">// 是否需要根据移动改变</span></span><br><span class="line">    <span class="keyword">boolean</span> isBetterPreAfter = <span class="keyword">true</span>; <span class="comment">// 是否需要比较前后</span></span><br><span class="line">    <span class="keyword">int</span> pointIndex;</span><br><span class="line">    <span class="keyword">int</span> lineIndex;</span><br><span class="line">    <span class="keyword">private</span> LineChartData lineChartData;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> vLength;<span class="comment">// 竖线长度</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> hLength;<span class="comment">// 横线长度</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">float</span> density;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> touchToleranceMargin;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> moveNumber = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> selectLineNumber = -<span class="number">1</span>; <span class="comment">// 现在选中的线条</span></span><br><span class="line">    <span class="keyword">private</span> vcDataChangeListener vcListener;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">vccharts</span><span class="params">(Context context, AttributeSet attrs)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(context, attrs);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            density = context.getResources().getDisplayMetrics().density;</span><br><span class="line">            touchToleranceMargin = ChartUtils.dp2px(density, DEFAULT_TOUCH_TOLERANCE_MARGIN_DP);</span><br><span class="line">            LogUtils.e(<span class="string">&quot;  touchToleranceMargin  &quot;</span> + touchToleranceMargin);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        touchHandler0 = <span class="keyword">new</span> ChartTouchHandler0(context, <span class="keyword">this</span>);</span><br><span class="line">        vccharts.<span class="keyword">this</span>.post(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">                <span class="comment">//在此处调用view.getMeasuredWidth()和getMeasuredHeight（）可得到正确值</span></span><br><span class="line">                <span class="keyword">int</span> width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();</span><br><span class="line">                <span class="keyword">int</span> height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();</span><br><span class="line"></span><br><span class="line">                hLength = width * <span class="number">0.75f</span>;          <span class="comment">// x轴长度</span></span><br><span class="line">                vLength = height * <span class="number">0.75f</span>;         <span class="comment">// y轴长度</span></span><br><span class="line"></span><br><span class="line">                LogUtils.e(<span class="string">&quot;  vLength &quot;</span> + vLength + <span class="string">&quot;  h &quot;</span> + hLength);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setVcListener</span><span class="params">(vcDataChangeListener vcListener)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.vcListener = vcListener;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">onTouchEvent</span><span class="params">(MotionEvent event)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">float</span> touchY = event.getY();</span><br><span class="line">        <span class="keyword">float</span> touchX = event.getX();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">switch</span> (event.getAction()) &#123;</span><br><span class="line">            <span class="keyword">case</span> MotionEvent.ACTION_DOWN:</span><br><span class="line">                LogUtils.e(<span class="string">&quot;                 按下了  &quot;</span>);</span><br><span class="line"></span><br><span class="line">                moveNumber = <span class="number">0</span>;</span><br><span class="line">                lineChartData = vccharts.<span class="keyword">this</span>.getLineChartData();</span><br><span class="line"></span><br><span class="line">                <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; lineChartData.getLines().size(); i++) &#123;</span><br><span class="line">                    Line line = lineChartData.getLines().get(i);</span><br><span class="line">                    <span class="keyword">int</span> pointRadius = ChartUtils.dp2px(density, line.getPointRadius());</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; line.getValues().size(); j++) &#123;</span><br><span class="line">                        PointValue pointValue = line.getValues().get(j);</span><br><span class="line">                        <span class="keyword">final</span> <span class="keyword">float</span> rawValueX = vccharts.<span class="keyword">this</span>.getChartComputator().computeRawX(pointValue.getX());</span><br><span class="line">                        <span class="keyword">final</span> <span class="keyword">float</span> rawValueY = vccharts.<span class="keyword">this</span>.getChartComputator().computeRawY(pointValue.getY());</span><br><span class="line">                        <span class="keyword">if</span> (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) &#123;</span><br><span class="line">                            LogUtils.e(<span class="string">&quot;     xuanzhong   xxx &quot;</span> + pointValue.getX() + <span class="string">&quot;        yyy   &quot;</span> + pointValue.getY() + <span class="string">&quot;           &quot;</span>);</span><br><span class="line">                            vccharts.<span class="keyword">this</span>.lineIndex = i;</span><br><span class="line">                            vccharts.<span class="keyword">this</span>.pointIndex = j;</span><br><span class="line">                            isMoveChange = <span class="keyword">true</span>;</span><br><span class="line">                            <span class="keyword">for</span> (Line line1 : lineChartData.getLines()) &#123;</span><br><span class="line">                                line1.setStrokeWidth(<span class="number">2</span>);</span><br><span class="line">                            &#125;</span><br><span class="line">                            line.setStrokeWidth(<span class="number">4</span>);</span><br><span class="line">                            selectLineNumber = i;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">case</span> MotionEvent.ACTION_MOVE:</span><br><span class="line">                <span class="keyword">if</span> (isMoveChange) &#123;</span><br><span class="line">                    <span class="keyword">float</span> y_max = <span class="number">10</span>;</span><br><span class="line">                    <span class="keyword">float</span> y_min = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//                    如果没有选中任何点</span></span><br><span class="line">                    <span class="keyword">if</span> (pointIndex == -<span class="number">1</span> || lineIndex == -<span class="number">1</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(event);</span><br><span class="line">                    &#125;</span><br><span class="line">                    lineChartData = vccharts.<span class="keyword">this</span>.getLineChartData();</span><br><span class="line">                    <span class="keyword">float</span> y = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getY();</span><br><span class="line">                    <span class="keyword">float</span> x = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getX();</span><br><span class="line">                    <span class="keyword">float</span> y_new = y_max - coordinateConversionY(touchY);</span><br><span class="line">                    <span class="keyword">if</span> (isBetterPreAfter) &#123;</span><br><span class="line">                        <span class="keyword">float</span> y_pre = y_min;</span><br><span class="line">                        <span class="keyword">float</span> y_after = y_max;</span><br><span class="line">                        <span class="keyword">if</span> (pointIndex &lt; lineChartData.getLines().get(lineIndex).getValues().size() - <span class="number">1</span>) &#123;</span><br><span class="line">                            y_after = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex + <span class="number">1</span>).getY();</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (pointIndex &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                            y_pre = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex - <span class="number">1</span>).getY();</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (y_new &lt; y_pre) &#123;</span><br><span class="line">                            y_new = y_pre;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (y_new &gt; y_after) &#123;</span><br><span class="line">                            y_new = y_after;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">if</span> (y_new &lt; y_min) &#123;</span><br><span class="line">                        y_new = y_min;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">if</span> (y_new &gt; y_max) &#123;</span><br><span class="line">                        y_new = y_max;</span><br><span class="line">                    &#125;</span><br><span class="line">                    lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).set(x, y_new);</span><br><span class="line"><span class="comment">//                    通知数据变化</span></span><br><span class="line">                    <span class="comment">/*List&lt;PointValue&gt; values = lineChartData.getLines().get(lineIndex).getValues();</span></span><br><span class="line"><span class="comment">                    float[] changeData = new float[values.size()];</span></span><br><span class="line"><span class="comment">                    for (int i = 0; i &lt; values.size(); i++) &#123;</span></span><br><span class="line"><span class="comment">                        changeData[i] = values.get(i).getY();</span></span><br><span class="line"><span class="comment">                    &#125;*/</span></span><br><span class="line">                    postInvalidate();</span><br><span class="line">                    <span class="comment">/*if (vcListener != null) &#123;</span></span><br><span class="line"><span class="comment">                        moveNumber++;</span></span><br><span class="line"><span class="comment">                        if (moveNumber % 3 == 0) &#123;</span></span><br><span class="line"><span class="comment">                            vcListener.DataChange(lineIndex, new ZuniData(changeData));</span></span><br><span class="line"><span class="comment">                        &#125;</span></span><br><span class="line"><span class="comment">                    &#125;*/</span></span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(event);</span><br><span class="line">            <span class="keyword">case</span> MotionEvent.ACTION_UP:</span><br><span class="line">                LogUtils.e(<span class="string">&quot;  move &quot;</span> + moveNumber);</span><br><span class="line">                <span class="keyword">if</span> (isMoveChange) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (vccharts.<span class="keyword">this</span>.pointIndex == -<span class="number">1</span> || vccharts.<span class="keyword">this</span>.lineIndex == -<span class="number">1</span>) &#123;</span><br><span class="line">                        isMoveChange = <span class="keyword">false</span>;</span><br><span class="line">                        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//                    通知数据变化</span></span><br><span class="line">                    List&lt;PointValue&gt; values = lineChartData.getLines().get(lineIndex).getValues();</span><br><span class="line">                    <span class="keyword">float</span>[] changeData = <span class="keyword">new</span> <span class="keyword">float</span>[values.size()];</span><br><span class="line">                    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; values.size(); i++) &#123;</span><br><span class="line">                        changeData[i] = values.get(i).getY();</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (vcListener != <span class="keyword">null</span>) &#123;</span><br><span class="line">                            vcListener.DataChange(lineIndex, <span class="keyword">new</span> ZuniData(changeData));</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    postInvalidate();</span><br><span class="line"></span><br><span class="line">                    isMoveChange = <span class="keyword">false</span>;</span><br><span class="line">                    vccharts.<span class="keyword">this</span>.pointIndex = -<span class="number">1</span>;</span><br><span class="line">                    vccharts.<span class="keyword">this</span>.lineIndex = -<span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">case</span> MotionEvent.ACTION_CANCEL:</span><br><span class="line">                <span class="keyword">if</span> (isMoveChange) &#123;</span><br><span class="line">                    isMoveChange = <span class="keyword">false</span>;</span><br><span class="line">                    vccharts.<span class="keyword">this</span>.pointIndex = -<span class="number">1</span>;</span><br><span class="line">                    vccharts.<span class="keyword">this</span>.lineIndex = -<span class="number">1</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">super</span>.onTouchEvent(event);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 把坐标从 y 轴的位置转换成实际的值</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">float</span> <span class="title">coordinateConversionY</span><span class="params">(<span class="keyword">float</span> y)</span> </span>&#123;</span><br><span class="line">        LogUtils.i(<span class="string">&quot;坐标从 y 轴的位置转换成实际的值&quot;</span> + (<span class="number">10</span> - <span class="number">0</span>) / vLength * y);</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">10</span> - <span class="number">0</span>) / vLength * y;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 把坐标从 x 轴的位置转换成实际的值</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">float</span> <span class="title">coordinateConversionX</span><span class="params">(<span class="keyword">float</span> x)</span> </span>&#123;</span><br><span class="line">        LogUtils.i(<span class="string">&quot;坐标从 x 轴的位置转换成实际的值&quot;</span> + (<span class="number">11</span> - <span class="number">0</span>) / hLength * x);</span><br><span class="line">        <span class="keyword">return</span> (<span class="number">11</span> - <span class="number">0</span>) / hLength * x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">isInArea</span><span class="params">(<span class="keyword">float</span> x, <span class="keyword">float</span> y, <span class="keyword">float</span> touchX, <span class="keyword">float</span> touchY, <span class="keyword">float</span> radius)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">float</span> diffX = touchX - x;</span><br><span class="line">        <span class="keyword">float</span> diffY = touchY - y;</span><br><span class="line">        <span class="keyword">return</span> Math.pow(diffX, <span class="number">2</span>) + Math.pow(diffY, <span class="number">2</span>) &lt;= <span class="number">2</span> * Math.pow(radius, <span class="number">2</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getSelectLineNumber</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> selectLineNumber;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSelectLineNumber</span><span class="params">(<span class="keyword">int</span> selectLineNumber)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.selectLineNumber = selectLineNumber;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isBetterPreAfter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> isBetterPreAfter;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBetterPreAfter</span><span class="params">(<span class="keyword">boolean</span> betterPreAfter)</span> </span>&#123;</span><br><span class="line">        isBetterPreAfter = betterPreAfter;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">vcDataChangeListener</span> </span>&#123;</span><br><span class="line">        <span class="function"><span class="keyword">void</span> <span class="title">DataChange</span><span class="params">(<span class="keyword">int</span> line_index, ZuniData ZuniData)</span></span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E3%80%90%E9%A1%B9%E7%9B%AE%E6%8E%A8%E8%8D%90%E3%80%91%E4%B8%80%E8%B5%B7%E6%9D%A5%E7%9C%8B%E4%B8%AA%E9%80%BC%E6%A0%BC%E5%84%BF%E5%BE%88%E9%AB%98%E7%9A%84%E4%B8%AA%E4%BA%BA%E7%AE%80%E5%8E%86%E9%A1%B9%E7%9B%AE/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E3%80%90%E9%A1%B9%E7%9B%AE%E6%8E%A8%E8%8D%90%E3%80%91%E4%B8%80%E8%B5%B7%E6%9D%A5%E7%9C%8B%E4%B8%AA%E9%80%BC%E6%A0%BC%E5%84%BF%E5%BE%88%E9%AB%98%E7%9A%84%E4%B8%AA%E4%BA%BA%E7%AE%80%E5%8E%86%E9%A1%B9%E7%9B%AE/" class="post-title-link" itemprop="url">【项目推荐】一起来看个逼格儿很高的个人简历项目</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>马上就要进入金三银四的招聘季了，虽说互联网寒冬很多公司都缩减了招聘规模，但是机会还是有的，提前预祝诸位想要面试的伙伴们面试成功，升值加薪！今天就给大家推荐一个简历项目，也希望大家能从这个项目中能学习到些东西。<br>因为我也是新接触，理解错误的地方还望指出。</p>
</blockquote>
<h2 id="项目简介"><a href="#项目简介" class="headerlink" title="项目简介"></a>项目简介</h2><p><img src="https://img-blog.csdnimg.cn/20190224065856768.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poZW5nX3dlaWNoYW8=,size_16,color_FFFFFF,t_70" alt="简历项目"><br>项目名称：Funday – 个人简历模板<br>作者：xiaodan （码云）<br>项目地址： <a target="_blank" rel="noopener" href="https://gitee.com/xiaodan_yu/resume.io">https://gitee.com/xiaodan_yu/resume.io</a><br>项目预览： <a target="_blank" rel="noopener" href="https://xiaodan_yu.gitee.io/resume.io">https://xiaodan_yu.gitee.io/resume.io</a></p>
<blockquote>
<p>【非你莫属】码云Pages技术简历/个性主页模板  一等奖<br>活动地址：<a target="_blank" rel="noopener" href="https://gitee.com/cool-resume">https://gitee.com/cool-resume</a></p>
</blockquote>
<h2 id="技术"><a href="#技术" class="headerlink" title="技术"></a>技术</h2><p>这个简历模板基于 <a target="_blank" rel="noopener" href="http://sc.chinaz.com/moban/170307198220.htm">Certy</a> 修改而来。保留了所有的样式，基于<strong>Jekyll</strong>重构了页面框架，并去掉了一些没有必要的内容，整合一页简历。</p>
<h2 id="开始研究"><a href="#开始研究" class="headerlink" title="开始研究"></a>开始研究</h2><h3 id="部署到码云"><a href="#部署到码云" class="headerlink" title="部署到码云"></a>部署到码云</h3><h4 id="1-首先把项目clone到自己的码云或者github仓库"><a href="#1-首先把项目clone到自己的码云或者github仓库" class="headerlink" title="1. 首先把项目clone到自己的码云或者github仓库"></a>1. 首先把项目clone到自己的码云或者github仓库</h4><p>可以通过fork，或者clone下载后上传。</p>
<h4 id="2-开启Pages服务"><a href="#2-开启Pages服务" class="headerlink" title="2. 开启Pages服务"></a>2. 开启Pages服务<img src="https://img-blog.csdnimg.cn/20190225072538125.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poZW5nX3dlaWNoYW8=,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"></h4><p>进入项目主页，在服务中选择Gitee Pages，然后点击开启服务即可。</p>
<blockquote>
<p>Gitee开启Pages服务需要绑定手机号。</p>
</blockquote>
<h3 id="本地调试"><a href="#本地调试" class="headerlink" title="本地调试"></a>本地调试</h3><p>如果想要本地查看效果，可以先搭建本地环境，进行调试。</p>
<h4 id="1-安装Jekyll"><a href="#1-安装Jekyll" class="headerlink" title="1.安装Jekyll"></a>1.安装Jekyll</h4><blockquote>
<p>jekyll官网：<a target="_blank" rel="noopener" href="https://www.jekyll.com.cn/">https://www.jekyll.com.cn/</a><br>jekyll是一个生成静态网页的工具，不需要数据库支持，可以免费部署在Github或者码云上。需要ruby环境。</p>
</blockquote>
<p>如果大家不需要本地调试的话，可以直接clone下项目，上传到自己的Github或者码云上，就可以查看效果。<br>安装的过程比较简单。</p>
<ul>
<li>下载和安装 Ruby： <a target="_blank" rel="noopener" href="http://www.ruby-lang.org/en/downloads/">http://www.ruby-lang.org/en/downloads/</a></li>
<li>安装jekyll：命令行里面输入：gem install jekyll</li>
<li>进入项目目录，输入jekyll serve，开启服务。</li>
<li>浏览器输入：<a target="_blank" rel="noopener" href="http://127.0.0.1:4000/resume.io/">http://127.0.0.1:4000/resume.io/</a> 查看<h3 id="2-项目结构："><a href="#2-项目结构：" class="headerlink" title="2. 项目结构："></a>2. 项目结构：</h3><table>
<thead>
<tr>
<th>文件</th>
<th>作用</th>
</tr>
</thead>
<tbody><tr>
<td>_includes/resumer_01-basic.html</td>
<td>修改基本信息</td>
</tr>
<tr>
<td>_includes/resumer_02-profetional.html</td>
<td>修改职业技能</td>
</tr>
<tr>
<td>_includes/resumer_03-education.html</td>
<td>修改教育经历</td>
</tr>
<tr>
<td>_includes/resumer_04-experience.html</td>
<td>修改工作经历</td>
</tr>
<tr>
<td>_includes/resumer_05-certification.html</td>
<td>获得证书</td>
</tr>
<tr>
<td>_includes/resumer_06-personal_project.html</td>
<td>修改个人作品</td>
</tr>
</tbody></table>
</li>
</ul>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>大家如果感兴趣的话，可以部署一下这个简历，也算是打造一个个人的简历名片。说不定会让面试官眼前一亮！哈~ 祝大家求职成功，找到好工作！</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://vchao.top/2021/08/25/%E3%80%90%E5%AE%89%E5%8D%93%E3%80%91%E5%94%A0%E7%82%B9%E5%84%BF%E4%BD%A0%E4%B8%8D%E4%B8%80%E5%AE%9A%E7%9F%A5%E9%81%93%E7%9A%84%E5%B0%8F%E7%9F%A5%E8%AF%86/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="vchao">
      <meta itemprop="description" content="怀揣好奇，梦想改变世界的菜鸟极客">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="乘风破浪">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/2021/08/25/%E3%80%90%E5%AE%89%E5%8D%93%E3%80%91%E5%94%A0%E7%82%B9%E5%84%BF%E4%BD%A0%E4%B8%8D%E4%B8%80%E5%AE%9A%E7%9F%A5%E9%81%93%E7%9A%84%E5%B0%8F%E7%9F%A5%E8%AF%86/" class="post-title-link" itemprop="url">【安卓】唠点儿你不一定知道的小知识</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2021-08-25 22:42:51 / 修改时间：22:37:20" itemprop="dateCreated datePublished" datetime="2021-08-25T22:42:51+08:00">2021-08-25</time>
            </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a>
                </span>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <blockquote>
<p>子曰: “学而时习之,不亦说乎?”</p>
</blockquote>
<h2 id="1-YYYY-和-yyyy-不一样"><a href="#1-YYYY-和-yyyy-不一样" class="headerlink" title="1. YYYY 和 yyyy 不一样"></a>1. YYYY 和 yyyy 不一样</h2><p>以 2019年12月31日 举例：<br>yyyy-MM-dd ：2019-12-31<br>YYYY-MM-dd ：2020-12-31</p>
<p>相信这个你已经知道了，很多大佬都写过详细的讲解文章，也可直接看官方说明<a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a> 。</p>
<p>简言之：Y 指的是 Week year，表示的是这个周所属的年份；y 表示的才是我们日常使用的年份。</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html">https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html</a></p>
</blockquote>
<h2 id="2-getReadableDatabase-不是以只读方式打开数据库"><a href="#2-getReadableDatabase-不是以只读方式打开数据库" class="headerlink" title="2. getReadableDatabase 不是以只读方式打开数据库"></a>2. getReadableDatabase 不是以只读方式打开数据库</h2><p>Android 中 <code>getWritableDatabase()</code> 和 <code>getReadableDatabase()</code> 方法都可以获取到 SQLiteDatabase 实例。<br>但<code>getReadableDatabase()</code>并不是以只读方式打开数据库，而是先执行<code>getWritableDatabase()</code>，失败的情况下才以只读方式打开数据库.。<br>源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> SQLiteDatabase <span class="title">getReadableDatabase</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 执行 getWritableDatabase() , 若出现异常,以只读方式打开数据库</span></span><br><span class="line">        <span class="keyword">return</span> getWritableDatabase();</span><br><span class="line">    &#125; <span class="keyword">catch</span> (SQLiteException e) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mName == <span class="keyword">null</span>) <span class="keyword">throw</span> e;  </span><br><span class="line">    &#125;</span><br><span class="line">    SQLiteDatabase db = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// ... </span></span><br><span class="line">        <span class="comment">// 以只读方式打开数据库</span></span><br><span class="line">        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);</span><br><span class="line">        <span class="comment">// ... </span></span><br><span class="line">        mDatabase = db;</span><br><span class="line">        <span class="keyword">return</span> mDatabase;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// ... </span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="3-子线程未必不能更新UI"><a href="#3-子线程未必不能更新UI" class="headerlink" title="3. 子线程未必不能更新UI"></a>3. 子线程未必不能更新UI</h2><p>Android的UI访问是没有加锁的，多线程访问时并不安全。所以规定只能在UI线程中访问UI。<br>负责检查线程的就是 <code>ViewRootImpl</code> 的 <code>checkThread()</code> 方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">checkThread</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (mThread != Thread.currentThread()) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> CalledFromWrongThreadException(</span><br><span class="line">                <span class="string">&quot;Only the original thread that created a view hierarchy can touch its views.&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然而 <code>ViewRootImpl</code> 的创建在 <code>onResume()</code> 回调之后。那么在 <code>onResume()</code> 之前，子线程里也是可以更新 UI 的 。<br>即使是 ViewRootImpl 创建后，只要不调用 <code>checkThread()</code>，子线程里更新也并不会报错。</p>
<blockquote>
<p>然而，大家开发的时候还是不要在子线程更新UI。</p>
</blockquote>
<h2 id="4-代码-new-的-View-没有id"><a href="#4-代码-new-的-View-没有id" class="headerlink" title="4. 代码 new 的 View 没有id"></a>4. 代码 new 的 View 没有id</h2><p>Android布局文件中通过 @+id 的方式，可以在 R文件 中生成对应的一个Int值，用于在运行时保证资源唯一性，但动态在代码中 new 的 View 没有 id 。<br>如果你有需求使用它的id，可以调用 View 的 <code>generateViewId()</code> 方法来生成 id(API17+) ，而非用随机数产生或手写一个具体的值。</p>
<h2 id="5-View-的-getContext-返回的未必是Activity"><a href="#5-View-的-getContext-返回的未必是Activity" class="headerlink" title="5. View 的 getContext 返回的未必是Activity"></a>5. View 的 getContext 返回的未必是Activity</h2><p>Activity中setContentView时一定是Activity;<br>通过 new View、View.inflate、LayoutInflater.inflate 这几种方式添加View，我们传参时传的是什么context, View中的就是什么Context.</p>
<p>在5.0系统版本以下的手机，且 Activity 是继承自 AppCompatActivity 的，那么View的getConext方法，返回的就不是Activity而是TintContextWrapper。</p>
<blockquote>
<p>参考博文：<a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5Xj6O9wV_EnynpdnB5mJ5A">https://mp.weixin.qq.com/s/5Xj6O9wV_EnynpdnB5mJ5A</a>  (南尘)</p>
</blockquote>
<h2 id="6-RemoteViews-和-View-没啥关系"><a href="#6-RemoteViews-和-View-没啥关系" class="headerlink" title="6. RemoteViews 和 View 没啥关系"></a>6. RemoteViews 和 View 没啥关系</h2><p>RemoteViews 提供了一组基础的操作用于跨进程更新，主要用于通知栏和桌面小部件的开发。从名称来看，感觉应该是一种远程的View。其实不然，源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RemoteViews</span> <span class="keyword">implements</span> <span class="title">Parcelable</span>, <span class="title">Filter</span> </span>&#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>总而言之，RemoteViews 就是为跨进程操作控件而提供一系列方法的一个类。</p>
<h2 id="7-boolean-类型占几个字节"><a href="#7-boolean-类型占几个字节" class="headerlink" title="7. boolean 类型占几个字节"></a>7. boolean 类型占几个字节</h2><p>Java中 boolean 表示的实际信息是一位：1表示true，0表示false。但是，Java规范 <a target="_blank" rel="noopener" href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html">数据类型文档</a> 没有精确定义内存中布尔变量的实际大小。</p>
<p>其大小与虚拟机相关，可以肯定的是，不会是 1 个 bit 。 Java 虚拟机的建议如下：<br>1.boolean 类型被编译成 int 类型来使用，占 4 个 byte 。<br>2.boolean 数组被编译成 byte 数组类型，每个 boolean 数组成员占 1 个 byte </p>
<blockquote>
<p>参考博文：<a target="_blank" rel="noopener" href="https://binkery.com/archives/346.html">https://binkery.com/archives/346.html</a></p>
</blockquote>
<h2 id="8-RecyclerView-布局文件可指定layoutManager跟spanCount"><a href="#8-RecyclerView-布局文件可指定layoutManager跟spanCount" class="headerlink" title="8. RecyclerView 布局文件可指定layoutManager跟spanCount"></a>8. RecyclerView 布局文件可指定layoutManager跟spanCount</h2><p>RecyclerView 布局文件可指定layoutManager跟spanCount</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">declare-styleable</span> <span class="attr">name</span>=<span class="string">&quot;RecyclerView&quot;</span>&gt;</span>        </span><br><span class="line">    <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;layoutManager&quot;</span> <span class="attr">format</span>=<span class="string">&quot;string&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;android:orientation&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;spanCount&quot;</span> <span class="attr">format</span>=<span class="string">&quot;integer&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;reverseLayout&quot;</span> <span class="attr">format</span>=<span class="string">&quot;boolean&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">attr</span> <span class="attr">name</span>=<span class="string">&quot;stackFromEnd&quot;</span> <span class="attr">format</span>=<span class="string">&quot;boolean&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">declare-styleable</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>它的属性attr里面可以指定layoutManager，spanCount,orientation的。不必我们在代码里设置。</p>
<h2 id="9-9-patch-图片是有-padding-的"><a href="#9-9-patch-图片是有-padding-的" class="headerlink" title="9.  9-patch 图片是有 padding 的"></a>9.  9-patch 图片是有 padding 的</h2><p>NinePatchDrawable 图形是一种可拉伸的位图，可用作视图的背景。Android 会自动调整图形的大小以适应视图的内容。包含一个额外的 1 像素边框，必须使用 9.png 扩展名将其保存在项目的 res/drawable/ 目录下。<br><img src="https://img-blog.csdnimg.cn/20200222222658343.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poZW5nX3dlaWNoYW8=,size_16,color_FFFFFF,t_70" alt="9patch图片"><br>线的作用：<br>左，上：定义允许复制图片的哪些像素来拉伸图片。<br>右，下：定义图片中允许放置视图内容的相对区域。</p>
<p>因此，9-patch 图片可能带有 padding，<strong>如果控件没有明确设置，图片的 padding 会作为控件的 padding</strong> 。</p>
<p>所以，有时候，<code>android:padding=&quot;0dp&quot;</code> 该写也是得写的。</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://developer.android.com/guide/topics/graphics/drawables#nine-patch">https://developer.android.com/guide/topics/graphics/drawables#nine-patch</a></p>
</blockquote>
<h2 id="10-硬件加速不是哪里都能开关"><a href="#10-硬件加速不是哪里都能开关" class="headerlink" title="10. 硬件加速不是哪里都能开关"></a>10. 硬件加速不是哪里都能开关</h2><p>硬件加速，直观上说就是依赖GPU实现图形绘制加速。由于 GPU 的引入不仅提高了绘制效率，还由于绘制机制的改变，极大地提高了界面内容改变时的刷新效率。<br>在 Android4.0 开始默认开启硬件加速，也可以手动控制打开关闭：</p>
<p>需要注意的是：<br><strong>硬件加速在Window级只能开不能关；View级只能关不能开。</strong></p>
<p><strong>Application 和 Activity 控制</strong><br>在 AndroidManifest 文件中 Application 或 Activity 节点添加</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">android:hardwareAccelerated=<span class="string">&quot;true&quot;</span></span><br></pre></td></tr></table></figure>
<p><strong>Window控制</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,</span><br><span class="line">WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)</span><br></pre></td></tr></table></figure>
<p><strong>View控制</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">view.setLayerType(View.LAYER_TYPE_SOFTWARE,<span class="keyword">null</span>);</span><br></pre></td></tr></table></figure>

<p><strong>查询是否开启硬件加速</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">View.isHardwareAccelerated()</span><br><span class="line">Canvas.isHardwareAccelerated()</span><br></pre></td></tr></table></figure>

<blockquote>
<p>参考博文：<a target="_blank" rel="noopener" href="https://www.jianshu.com/p/9cd7097a4fcf">https://www.jianshu.com/p/9cd7097a4fcf</a></p>
</blockquote>
<h2 id="11-用-getVisibility-判断用户是否能看见并不好"><a href="#11-用-getVisibility-判断用户是否能看见并不好" class="headerlink" title="11. 用 getVisibility() 判断用户是否能看见并不好"></a>11. 用 getVisibility() 判断用户是否能看见并不好</h2><p>getVisibility()只判断它自身是否是显示状态。但是如果它的父级不可见呢？<br>用 <code>isShown()</code> 方法更合适些，会先判断当前 View 的flag， 然后循环拿到父View，判断是不是可见。只要有一个是不可见的，就返回false。<br>源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isShown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    View current = <span class="keyword">this</span>;</span><br><span class="line">    <span class="comment">//noinspection ConstantConditions</span></span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> ((current.mViewFlags &amp; VISIBILITY_MASK) != VISIBLE) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        ViewParent parent = current.mParent;</span><br><span class="line">        <span class="keyword">if</span> (parent == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>; <span class="comment">// We are not attached to the view root</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!(parent <span class="keyword">instanceof</span> View)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        current = (View) parent;</span><br><span class="line">    &#125; <span class="keyword">while</span> (current != <span class="keyword">null</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<hr>
<p>很多知识点都来自于网上大佬们的博客，有些已经忘记从哪里看到的了，在此向大佬们致敬！</p>
<p>如果本文对你有所帮助，还望可以点个赞哈~~</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="page-number current">2</span><a class="page-number" href="/page/3/">3</a><span class="space">&hellip;</span><a class="page-number" href="/page/5/">5</a><a class="extend next" rel="next" href="/page/3/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">vchao</p>
  <div class="site-description" itemprop="description">怀揣好奇，梦想改变世界的菜鸟极客</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">46</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
        <span class="site-state-item-count">4</span>
        <span class="site-state-item-name">分类</span>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">27</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        
  <div class="beian"><a href="https://beian.miit.gov.cn/" rel="noopener" target="_blank">冀ICP备 17022751号-1 </a>
      <img src="https://beian.miit.gov.cn" style="display: inline-block;">
  </div>

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">vchao</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://muse.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/muse.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
